Skip to content

feat(models): add Inkling VLM MoE support - #3095

Merged
HuiyingLi merged 11 commits into
mainfrom
hemild/feat/inkling-support
Jul 18, 2026
Merged

feat(models): add Inkling VLM MoE support#3095
HuiyingLi merged 11 commits into
mainfrom
hemild/feat/inkling-support

Conversation

@hemildesai

@hemildesai hemildesai commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a native Inkling VLM MoE implementation, processor setup, and exact raw-checkpoint state-dict conversion
  • register Inkling and add a MedPix fine-tuning recipe using PP8, EP32, FSDP2, non-reentrant activation checkpointing, and torch_mm experts
  • keep Inkling's fp32 short-convolution parameters in model-local _fp32_params holders that reuse the existing FSDP holder path; components/moe/parallelizer.py is unchanged from main
  • support flat multimodal patch batches through the shared VLM collation and pipeline-media paths
  • support PyTorch 2.12 pipeline output metadata and reuse PR feat(moe): DeepEP v2 dispatcher, Torch 2.13 PP, and selectable EP variants #2930's centralized PP neighbor-communicator warmup and static-metadata selection; Inkling has no model- or recipe-specific warmup hook
  • pin the Transformers commit containing upstream Inkling support and add model coverage documentation

Validation

  • 100-step MedPix fine-tuning after final simplification
    • Slurm job 14060480: 256 H100 GPUs, PP8/EP32, global batch 256, sequence length 2048
    • completed steps 0-99 in 34:23 with exit 0:0 using NCCL's default communicator blocking behavior
    • loss decreased from 2.3000 to 1.7906; final grad norm 0.7824, memory 58.53 GiB, and throughput 4,415.29 tokens/s
    • matched the previous 100-step baseline, which ended at loss 1.7898, grad norm 0.8266, memory 58.54 GiB, and throughput 4,316.08 tokens/s
  • focused unit, parity, PP warmup, mesh, VLM batching, and MoE sharding suite: 122 passed in 8.48s (Slurm job 14060405, exit 0:0)
  • real two-GPU NCCL PP warmup, forward/backward, static metadata, and sequence-length reset smoke with default communicator blocking (Slurm job 14060410, exit 0:0)
  • EP2 + FSDP2 + non-reentrant activation-checkpointing optimizer smoke using the unchanged main MoE parallelizer (Slurm job 14057832, exit 0:0)
  • four-layer forward/backward parity and full checkpoint validation
  • all 1,392 non-MTP checkpoint keys and converted tensor shapes validated in both directions
  • pre-commit hooks, Ruff, uv lock --check, compileall, and git diff --check

Caveat

  • MTP tensors are intentionally ignored because the standard Hugging Face conditional-generation model does not expose the MTP training path.

@copy-pr-bot

copy-pr-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

Copy link
Copy Markdown
Contributor

@hemildesai

Copy link
Copy Markdown
Contributor Author

/claude review

@hemildesai

Copy link
Copy Markdown
Contributor Author

/ok to test 7fab517

@claude

claude Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Reviewed the Inkling VLM MoE onboarding (model/layers/adapter/processing, registry, collate + PP media, MoE parallelizer fp32 handling, PP functional metadata, grad-norm warmup, recipe/docs). Accounted for all 26 changed files; uv.lock excluded per policy.

Strong points worth calling out: registry entry + ModelCapabilities(supports_pp=True, supports_ep=True) match the PP8/EP32 recipe; whole-model cast goes through cast_model_to_dtype (honoring _keep_in_fp32_modules_strict) rather than a raw self.to(dtype); router scoring is pinned to fp32; and the changes are backed by CPU parity tests — state-dict round-trip, forward-logit KL < 1e-3, per-parameter backward gradient parity, multimodal tower parity, and a two-stage pipeline forward parity test.

Findings are minor (3 inline): tensor-contract docstrings are missing/incomplete on new tensor-accepting functions with nonstandard interleaved/flat layouts (inkling_swiglu, InklingDenseMLP.forward, chunk_inkling_media). These are documentation gaps, not correctness bugs.

No blocking issues found.

Comment thread nemo_automodel/components/models/inkling/layers.py
Comment thread nemo_automodel/components/datasets/vlm/pp_media.py Outdated
Comment thread nemo_automodel/components/models/inkling/layers.py
hemildesai and others added 2 commits July 15, 2026 23:02
Signed-off-by: hemildesai <hemild@nvidia.com>
Signed-off-by: NeMo Bot <nemo-bot@nvidia.com>
@hemildesai
hemildesai force-pushed the hemild/feat/inkling-support branch from 7fab517 to 15724fb Compare July 16, 2026 06:03
hemildesai and others added 4 commits July 15, 2026 23:39
Signed-off-by: hemildesai <hemild@nvidia.com>
Signed-off-by: hemildesai <hemild@nvidia.com>
Signed-off-by: Yuhe Zhang <yuhez@nvidia.com>
@yuhezhang-ai

Copy link
Copy Markdown
Contributor

Hi @hemildesai Thanks for the great work. Since #2998, my earlier TieSupport refactor, has merged, every model with an LM head now needs to declare its tying policy. I added a small follow-up marking Inkling as UNTIED_ONLY with focused tests. This matches its existing separate embed/unembed weights and should not change any of your implementation’s behavior.

@HuiyingLi

Copy link
Copy Markdown
Contributor

/ok to test b4336ee

@hemildesai

Copy link
Copy Markdown
Contributor Author

/claude review

@hemildesai

Copy link
Copy Markdown
Contributor Author

/ok to test 1dafb36

@claude

claude Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Light review complete. This is a well-constructed model onboarding PR (Inkling multimodal MoE VLM) plus a Torch 2.12 pipelining compatibility path. Coverage is strong: state-dict round-trip, forward/backward gradient parity, multimodal tower parity, two-stage pipeline forward parity, and the static-metadata warm-up path all have focused CPU tests. ModelCapabilities correctly flips supports_pp/supports_ep to True, cast_model_to_dtype is used (not raw self.to()), and fp32 holders for short-convs / router correction bias are preserved via _keep_in_fp32_modules_strict.

One minor finding posted inline:

  • _set_stage_metas in pipelining/functional.py accepts tensor-tuple args (inputs_meta/outputs_meta) without documenting their layout, which the repo's tensor-contract docstring invariant requires.

No correctness, security, or gradient-handling issues found.

if stage.group_size > 2:
edges.add((0, stage.group_size - 1))

edge_phases: list[list[tuple[int, int]]] = []

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.

This new production helper accepts inputs_meta / outputs_meta tensor tuples but its docstring doesn't document their layout, which the repo's tensor-contract invariant requires for functions accepting tensor inputs. Please add a Google-style Args section, e.g.:

Args:
    stage: The pipeline stage to configure.
    inputs_meta: Meta-device tensors describing the stage's forward inputs —
        `[microbatch, sequence]` int64 for the first stage, else
        `[microbatch, sequence, hidden]` in the model dtype.
    outputs_meta: Meta-device tensors describing the stage's forward outputs —
        `[microbatch, sequence, vocab]` for a stage holding the LM head, else
        `[microbatch, sequence, hidden]`.

With transformers pinned at main's 5.12.1 (no Inkling upstream), the Inkling
unit tests are importorskip-guarded and skip in CI, so the new
nemo_automodel/components/models/inkling code has ~0% patch coverage and trips
the default 80% codecov/patch gate (10.82% of diff hit).

Follow the existing Bagel precedent: exclude the inkling package from the
default patch group and add an informational-only inkling group. Coverage
activates automatically once a transformers release shipping Inkling lets the
guarded tests run.

Signed-off-by: hemildesai <hemild@nvidia.com>
@hemildesai

Copy link
Copy Markdown
Contributor Author

/claude review

@hemildesai

Copy link
Copy Markdown
Contributor Author

/ok to test 5172c25

@claude

claude Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Light review complete — 19 changed files reviewed (uv.lock excluded).

This PR onboards the Inkling multimodal MoE VLM (model wrapper, EP MoE layers, state-dict adapter, processor helper), extends the VLM collate/PP-media path, and adds Torch 2.12 pipeline-stage static-metadata + neighbor-warmup compatibility. Overall the implementation looks solid and unusually well tested:

  • State-dict adapter round-trip is asserted exact, and forward + backward logit parity vs HF is checked at KL < 1e-3 with per-parameter gradient parity on gate/experts.
  • Multimodal (vision + audio) tower parity and a two-stage pipeline forward-parity test are included.
  • Router scoring is kept in fp32, the checkpoint's interleaved fused gate/up layout is preserved, short-conv / correction-bias fp32 holders are cast via cast_model_to_dtype (not raw .to), ModelCapabilities correctly declares supports_pp=True/supports_ep=True, and the registry entry is present.
  • The generalized chunk_step3_media flat/sample path is covered by an updated unit test, and the InklingProcessor label/collate paths have focused CPU tests.

One minor in-scope finding posted inline: the overridden InklingTextModel.forward (and the model forward) lack a Google-style Args section documenting tensor layouts, which matters here because input_ids carries either token ids or float hidden states depending on the pipeline stage.

No correctness, security, or config-boundary issues found.

past_key_values: Any | None = None,
inputs_embeds: torch.FloatTensor | None = None,
use_cache: bool | None = None,
**kwargs: Any,

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.

This overriding forward has no docstring, yet it materially changes tensor handling versus the HF base: input_ids here may carry either token ids or float hidden states (the pipeline-stage path sets inputs_embeds = input_ids when input_ids.dtype.is_floating_point), which is a non-obvious dual contract. Per the repo's tensor-contract rule, please document the layouts of the tensor inputs, e.g.:

Args:
    input_ids: Tensor of shape [batch, sequence] of token ids on the
        first stage; on later pipeline stages without an embedding table
        it instead carries float hidden states of shape
        [batch, sequence, hidden].
    inputs_embeds: Optional Tensor of shape [batch, sequence, hidden].
    attention_mask: Tensor of shape [batch, sequence] or a precomputed
        per-attention-type mask mapping.

The public forward at line 278 (pixel_values, input_ids, inputs_embeds, ...) similarly documents no input layouts and would benefit from the same Args section.

@hemildesai

Copy link
Copy Markdown
Contributor Author

/claude review

@hemildesai

Copy link
Copy Markdown
Contributor Author

/ok to test d754b04

@copy-pr-bot

copy-pr-bot Bot commented Jul 18, 2026

Copy link
Copy Markdown

/ok to test d754b04

@hemildesai, there was an error processing your request: E2

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/2/

@claude

claude Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review summary — reviewed all 19 changed files (excluding uv.lock). This is a well-structured onboarding of the Inkling multimodal MoE VLM: the model wrapper reuses HF towers, adds an EP InklingMoE, a state-dict adapter with an exact round-trip, plus a Torch 2.12 PP static-metadata compatibility path and generalized chunk_step3_media flat-pixel handling.

Test coverage is strong and behavior-asserting: CPU state-dict round-trip, forward-logit parity (KL < 1e-3), backward gradient parity across gate/experts/embeddings, multimodal tower parity, and a two-stage PP forward-parity test. ModelCapabilities correctly declares supports_pp/supports_ep, RoPE stays HF-owned, and cast_model_to_dtype is used with strict fp32 holders for the router bias and short convolutions — so the low-precision-RoPE and checkpoint-free-init concerns are handled.

Findings (2, non-blocking): both are tensor-contract docstring gaps on the two new public forward overrides in inkling/model.py (InklingTextModel.forward and InklingForConditionalGeneration.forward), posted inline. These new overrides accept/return tensors whose layouts — especially the PP-stage-dependent input/output reinterpretation — the repo requires be documented, and they diverge from the base contract so cannot simply reference it.

No correctness, security, gradient-domain, or API-surface issues found. The num_patches addition in default_collate_fn is appropriately guarded (image_token_id present + 5-D pixel_values) so it does not perturb existing Qwen/Gemma paths.

inputs_embeds: torch.FloatTensor | None = None,
use_cache: bool | None = None,
**kwargs: Any,
) -> BaseModelOutputWithPast:

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.

InklingTextModel.forward is a new override that materially changes the base contract (it reinterprets input_ids as pre-computed hidden states on non-embedding PP stages, per line 87-90), so it can't defer to HF's documented contract and needs its own tensor-layout docstring. Per the repo's tensor-contract rule, add a Google-style Args/Returns documenting at least:

Args:
    input_ids: Tensor of shape [batch, sequence] of token ids on the first
        stage; on later PP stages a float hidden-states tensor of shape
        [batch, sequence, hidden] passed in this slot (input_ids is then set
        to None internally).
    inputs_embeds: Optional Tensor of shape [batch, sequence, hidden].
    attention_mask: Tensor of shape [batch, sequence] or a mapping of
        per-attention-type masks.
Returns:
    BaseModelOutputWithPast whose last_hidden_state has shape
    [batch, sequence, hidden].

logits_to_keep: int | torch.Tensor = 0,
**kwargs: Any,
) -> Any:
"""Run the standard Inkling forward or its pipeline-stage equivalent."""

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.

This new public forward takes several tensor inputs (input_ids, pixel_values, inputs_embeds, audio_input_ids, ...) and returns either hidden states [batch, sequence, hidden] (non-last PP stage) or logits [batch, sequence, unpadded_vocab] (last stage), but the one-line docstring documents none of these layouts. Per the repo's tensor-contract rule, add a Google-style Args/Returns giving each tensor input's shape (e.g. input_ids: Tensor of shape [batch, sequence]; pixel_values: Tensor of shape [num_images, temporal_patch, patch, patch, channels]) and describing the two possible return layouts, since callers on different PP stages consume different outputs.

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.

3 participants