Expand description
Kimi K3’s MoonViT-V2 vision encoder: patch embedding -> N transformer encoder layers (RMSNorm, self-attention with 2D RoPE, gated-MLP-free plain MLP2 feed-forward) -> a patch-merger projector into the text decoder’s embedding space. Genuinely new territory for ferrox, which has been text-only until now.
Transcribed directly from real reference source fetched live from the
model repo (not guessed, not derived by analogy to other ViT/RoPE
designs): moonshotai/Kimi-K3’s modeling_kimi_k3.py
(MoonVision3dPatchEmbed, Rope2DPosEmbRepeated, apply_rope,
MoonViTEncoderLayer, MoonViT3dEncoder, tpool_patch_merger,
PatchMergerMLPV2) and its real config.json’s vision_config
(norm_type='rmsnorm', activation_func='gelu_pytorch_tanh',
attn_bias=False, linear_bias=False, patch_embed_proj_bias=False,
patch_size=14, vt_hidden_size=1024, vt_num_attention_heads=12,
qkv_hidden_size=1536, vt_intermediate_size=4096,
vt_num_hidden_layers=27, merge_kernel_size=[2,2],
mm_projector_type='patchmergerv2', projector_ln_eps=1e-5).
Deliberately scoped, and disclosed as such, to the common case rather than every real code path:
- Single image, single frame (
t=1) – no multi-frame video input, so the real code’s temporal sincos position-embedding addition and the patch-merger’s temporal-mean pooling are both no-ops here, not implemented as general code paths. - The caller’s patch grid is assumed to exactly match the
positional-embedding grid it was constructed with, so the real
code’s bicubic/bilinear interpolation branch (
get_rope_shape, for when input resolution differs from the pretrained grid) is never exercised and not implemented. - One image at a time – no variable-length multi-image batch packing
(
cu_seqlens); process a full patch sequence for one image per call. - The real multimodal splicing of vision output into the text
decoder’s input sequence (
KimiK3ForConditionalGeneration.forward) is out of scope here – this module only implements the vision tower itself.
Two real, non-obvious facts confirmed by reading source rather than assuming standard ViT/RoPE conventions:
MoonVision3dPatchEmbed.forwardreassignsxbefore callingx.size(0)in the same statement (x = self.proj(x).view(x.size(0), -1)), but Python evaluates the right-hand side of an assignment before rebinding the name, sox.size(0)refers to the original, pre-convx– meaning the real input to this module is already a batch of N individually pre-extracted[C, patch_size, patch_size]patches (one “image” per patch), not one whole image run through a strided conv. AConv2d(kernel_size=patch_size, stride=patch_size)applied to an input exactlypatch_sizewide/tall is mathematically just one linear projection of the flattened patch – so patch embedding here is a single sharedLinear(in_dim*patch_size*patch_size, out_dim, bias=false)applied per patch (WeightMatrix::apply), not a general strided convolution.- The 2D RoPE (
apply_rope) rotates consecutive real-value pairs ((x[2i], x[2i+1]), the original RoFormer/GPT-NeoX convention viaview_as_complex), NOT the “rotate half” convention ((x[i], x[i+dim/2]))ferrox_core::attention::apply_ropeimplements for the text decoder’s RoPE – a distinct function is needed here, not a reuse of that one. Within each complex pair indexjin[0, head_dim/2),_precompute_freqs_cisinterleaves width- and height-based angles: evenjuses the patch’s width coordinate, oddjuses its height coordinate (confirmed by reading the actualtorch.cat(...).reshape(...)code, which contradicts that same function’s own docstring – code wins).
Not yet wired into Decoder or spliced into the text embedding
sequence. Tested here against synthetic weights, cross-validated
against an independent Python transcription of the same real
algorithm.
Structs§
Functions§
- embed_
patches - Embeds a full image’s patches (already extracted as flattened
in_dim*patch_size*patch_sizepixel vectors, row-major(h, w)order) and adds the learned position embedding. Returns[n_patches, hidden_dim]flattened. - encoder_
forward - Runs the full encoder (patch embed + every layer + final norm) for
one image. Returns
[n_patches, hidden_dim]flattened. - patch_
merge tpool_patch_mergerfor the single-frame (t=1) case: groups spatially-adjacentmerge_kh x merge_kwpatches (row-major(h, w)) into one merged patch each, without the temporal mean (a no-op att=1). Returns[num_merged, merge_kh*merge_kw*hidden_dim]flattened – already contiguous per merged patch, ready for the projector.- project_
merged_ patches PatchMergerMLPV2: per merged patch,Linear -> GELU (erf) -> Linear -> RMSNorm.mergedis[num_merged, merge_kh*merge_kw*hidden_dim]flattened (patch_merge’s output); returns[num_merged, text_hidden]flattened.