Model Architecture

Transformer, MoE, and LoRA Implementation

๐Ÿ“ฆ model/ ๐Ÿ Python ๐Ÿ”– 83e52f6 ๐Ÿ“… 2026-03-26

โฑ Estimated reading time: 8 min

3
Files
12
Classes / Functions
~520
Lines of Code
Python
Language
๐Ÿ”ต Transformer ยท GQA ๐ŸŸข RoPE ยท YaRN ๐ŸŸก MoE ยท Top-K Gating ๐Ÿ”ด LoRA ยท Low-Rank
๐Ÿญ Think of it like a factory assembly line. The model/ directory is the blueprint for MiniMind's neural "factory." Raw token IDs enter the conveyor belt (embed_tokens), pass through a series of identical processing stations (MiniMindBlock), each of which inspects context (Attention) and refines features (FeedForward). At the end of the line, a quality-control head (lm_head) stamps out the next-token prediction. The MoE option swaps a single FFN station for a team of specialist workers, each activated only when their expertise is needed. LoRA lets you fine-tune the factory by clipping small adapter jigs onto existing machinery โ€” no need to retool the whole line.

1 · Architecture Overview

MiniMind follows a decoder-only Transformer design compatible with Hugging Face's PreTrainedModel. The class hierarchy reflects clean separation of concerns: configuration lives in MiniMindConfig, the backbone in MiniMindModel, and the language-modelling head in MiniMindForCausalLM.

๐Ÿ” Click to zoom
classDiagram
    class MiniMindForCausalLM {
        +model: MiniMindModel
        +lm_head: Linear
        +forward(input_ids, labels)
        +weight tying
    }
    class MiniMindModel {
        +embed_tokens: Embedding
        +layers: List~MiniMindBlock~
        +norm: RMSNorm
        +freqs_cos, freqs_sin
    }
    class MiniMindBlock {
        +attention: Attention
        +feed_forward: FFN or MoE
        +attention_norm: RMSNorm
        +ffn_norm: RMSNorm
    }
    class Attention {
        +q_proj, k_proj, v_proj, o_proj
        +GQA: num_kv_heads=2
        +flash_attn support
        +KV cache
    }
    class FeedForward {
        +gate_proj: Linear
        +up_proj: Linear
        +down_proj: Linear
        +SwiGLU activation
    }
    class MOEFeedForward {
        +gate: MoEGate
        +experts: List~FeedForward~
        +shared_experts: FeedForward
    }
    class MoEGate {
        +weight: Linear
        +topk scoring
        +aux_loss
    }
    class RMSNorm {
        +weight: Parameter
        +eps: float
    }
    class MiniMindConfig {
        +hidden_size, num_layers
        +use_moe, n_routed_experts
        +rope_theta, flash_attn
    }
    class LoRA {
        +A: Linear (rank)
        +B: Linear (rank)
    }
    MiniMindForCausalLM --> MiniMindModel
    MiniMindModel --> MiniMindBlock
    MiniMindBlock --> Attention
    MiniMindBlock --> FeedForward
    MiniMindBlock --> MOEFeedForward
    MOEFeedForward --> MoEGate
    MOEFeedForward --> FeedForward : experts
    MiniMindBlock --> RMSNorm
    MiniMindForCausalLM ..> MiniMindConfig
    LoRA ..> Attention : adapts projections
    

2 · Key Classes & Functions

#NameFileDescription
1MiniMindConfigmodel_minimind.pyDataclass holding all hyperparameters; inherits PretrainedConfig.
2RMSNormmodel_minimind.pyRoot-mean-square layer normalisation with learnable scale.
3precompute_freqs_cismodel_minimind.pyBuilds cosine/sine tables for RoPE; supports YaRN scaling.
4apply_rotary_pos_embmodel_minimind.pyRotates Q/K tensors using precomputed frequencies.
5repeat_kvmodel_minimind.pyRepeats K/V heads to match Q head count in GQA.
6Attentionmodel_minimind.pyMulti-head attention with grouped-query support and KV cache.
7FeedForwardmodel_minimind.pySwiGLU feed-forward network with auto-computed hidden dim.
8MoEGatemodel_minimind.pyTop-K softmax gating with auxiliary load-balancing loss.
9MOEFeedForwardmodel_minimind.pyMixture-of-Experts FFN with routed + shared experts.
10MiniMindBlockmodel_minimind.pyOne Transformer layer: pre-norm โ†’ attention โ†’ FFN with residuals.
11MiniMindModelmodel_minimind.pyFull decoder backbone: embedding + N blocks + final norm.
12MiniMindForCausalLMmodel_minimind.pyCausal LM wrapper: backbone + lm_head + loss + generation.
13LoRAmodel_lora.pyLow-rank adapter module (AยทB decomposition).
14apply_loramodel_lora.pyInjects LoRA adapters into all square Linear layers.
15load_lora / save_loramodel_lora.pySerialization helpers for LoRA-only checkpoints.

3 · Forward-Pass Data Flow

The following diagram traces a single forward pass through MiniMindForCausalLM, from raw token IDs to cross-entropy loss.

๐Ÿ” Click to zoom
sequenceDiagram
    participant Caller as Trainer / generate()
    participant CLM as MiniMindForCausalLM
    participant Model as MiniMindModel
    participant Embed as embed_tokens
    participant Block as MiniMindBlock ร—N
    participant Attn as Attention (GQA)
    participant FFN as FeedForward / MoE
    participant Norm as RMSNorm (final)
    participant Head as lm_head

    Caller->>CLM: input_ids, labels, past_kv
    CLM->>Model: input_ids, attention_mask, past_kv
    Model->>Embed: input_ids
    Embed-->>Model: hidden_states (B,T,D)
    loop For each layer i = 0..N-1
        Model->>Block: hidden_states, freqs, mask, kv_cache[i]
        Block->>Attn: RMSNorm(hidden_states)
        Attn-->>Block: attn_out, updated kv_cache
        Block->>Block: hidden_states += attn_out
        Block->>FFN: RMSNorm(hidden_states)
        FFN-->>Block: ffn_out [+ aux_loss if MoE]
        Block-->>Model: hidden_states += ffn_out
    end
    Model->>Norm: hidden_states
    Norm-->>Model: normed_states
    Model-->>CLM: normed_states, kv_caches, aux_loss
    CLM->>Head: normed_states[:, -logits_to_keep:]
    Head-->>CLM: logits (B,T,V)
    CLM->>CLM: CrossEntropyLoss + aux_loss
    CLM-->>Caller: CausalLMOutputWithPast
    

4 · Module Interactions

How model/ connects with the rest of the MiniMind project:

External ModuleDirectionDescription
train_pretrain.pyโ†’ modelInstantiates MiniMindForCausalLM, calls forward(), reads loss + aux_loss.
train_full_sft.pyโ†’ modelFine-tunes full model weights; same forward path as pre-training.
train_lora.pyโ†’ model, loraCalls apply_lora(model), trains only LoRA params, uses save_lora().
train_grpo.pyโ†’ modelUses logits_to_keep param for memory-efficient GRPO training.
dataset.pyโ† modelProvides tokenized input_ids and labels consumed by the model.
eval_model.pyโ†’ modelLoads checkpoint, calls model.generate() via GenerationMixin.
Hugging Face Hubโ†” modelConfig + weights saved/loaded via PreTrainedModel API.

5 · Deep-Dive Hub

๐Ÿ”ต

Attention Mechanism

Grouped-Query Attention with 8 Q-heads and 2 KV-heads. Supports Flash Attention via scaled_dot_product_attention and autoregressive KV caching for fast generation.

๐ŸŸก

MoE Routing

Top-2 gating over 4 routed experts plus 1 shared expert. Softmax scoring with Kaiming-uniform init. Auxiliary load-balancing loss at token or sequence level keeps experts evenly utilised.

๐ŸŸข

RoPE & YaRN

Rotary position embeddings with ฮธ=1e6 and max 32768 positions. YaRN scaling (factor 16, ฮฒ_fast=32, ฮฒ_slow=1) for long-context extrapolation: f'(i) = f(i)ยท((1โˆ’ฮณ) + ฮณ/s).

๐Ÿ”ด

LoRA Adapter

Rank-r decomposition injected into all square projection layers. A-matrix Gaussian-init, B-matrix zero-init ensures no change at start. Only ~0.5% extra parameters.

6 · Directory Map

model/ โ”œโ”€โ”€ __init__.py # Package init (empty) โ”œโ”€โ”€ model_minimind.py # Core transformer (~464 lines) โ”‚ โ”œโ”€โ”€ MiniMindConfig # Configuration dataclass โ”‚ โ”œโ”€โ”€ RMSNorm # Layer normalization โ”‚ โ”œโ”€โ”€ precompute_freqs_cis # RoPE frequency tables โ”‚ โ”œโ”€โ”€ apply_rotary_pos_emb # Rotary embedding application โ”‚ โ”œโ”€โ”€ repeat_kv # GQA key/value repetition โ”‚ โ”œโ”€โ”€ Attention # Multi-head GQA attention โ”‚ โ”œโ”€โ”€ FeedForward # SwiGLU FFN โ”‚ โ”œโ”€โ”€ MoEGate # Top-K expert gating โ”‚ โ”œโ”€โ”€ MOEFeedForward # MoE layer โ”‚ โ”œโ”€โ”€ MiniMindBlock # Transformer block โ”‚ โ”œโ”€โ”€ MiniMindModel # Full decoder backbone โ”‚ โ””โ”€โ”€ MiniMindForCausalLM # Causal LM head + loss โ””โ”€โ”€ model_lora.py # LoRA adapters (~54 lines) โ”œโ”€โ”€ LoRA # Low-rank module โ”œโ”€โ”€ apply_lora # Injection helper โ”œโ”€โ”€ load_lora # Weight loading โ””โ”€โ”€ save_lora # Weight saving

7 · Configuration Reference

All knobs live in MiniMindConfig(PretrainedConfig). Values shown are the defaults.

ParameterDefaultDescription
hidden_size512 / 640 / 768Embedding dimension (varies by model size).
num_hidden_layers8 / 16Number of Transformer blocks.
num_attention_heads8Number of query heads per layer.
num_key_value_heads2Number of KV heads (grouped-query attention).
vocab_size6400Vocabulary size used by the tokenizer.
max_position_embeddings32768Maximum sequence length supported.
rope_theta1e6Base frequency for rotary position embeddings.
use_moeFalseEnable Mixture-of-Experts feed-forward layers.
num_experts_per_tok2Top-K experts activated per token.
n_routed_experts4Total number of routed expert FFNs.
n_shared_experts1Number of always-active shared experts.
scoring_func"softmax"Gating score function for MoE routing.
aux_loss_alpha0.01Weight of auxiliary load-balancing loss.
flash_attnTrueUse PyTorch scaled_dot_product_attention.
dropout0.0Dropout probability (disabled by default).
rms_norm_eps1e-5Epsilon for RMSNorm numerical stability.
hidden_act"silu"Activation function (ACT2FN lookup).
inference_rope_scalingYaRN dictYaRN config: factor=16, beta_fast=32, beta_slow=1.

8 · Code Examples

8.1 · SwiGLU Feed-Forward (from model_minimind.py)

class FeedForward(nn.Module):
    def __init__(self, config: MiniMindConfig):
        super().__init__()
        hidden_dim = int(config.hidden_size * 8 / 3)
        hidden_dim = 64 * ((hidden_dim + 63) // 64)  # round to 64
        self.gate_proj = nn.Linear(config.hidden_size, hidden_dim, bias=False)
        self.up_proj   = nn.Linear(config.hidden_size, hidden_dim, bias=False)
        self.down_proj = nn.Linear(hidden_dim, config.hidden_size, bias=False)
        self.act_fn    = ACT2FN[config.hidden_act]

    def forward(self, x):
        return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))

8.2 · LoRA Injection (from model_lora.py)

class LoRA(nn.Module):
    def __init__(self, in_features, out_features, rank=8):
        super().__init__()
        self.A = nn.Linear(in_features, rank, bias=False)
        self.B = nn.Linear(rank, out_features, bias=False)
        nn.init.normal_(self.A.weight, std=1/rank)
        nn.init.zeros_(self.B.weight)

    def forward(self, x):
        return self.B(self.A(x))

def apply_lora(model, rank=8):
    for name, module in model.named_modules():
        if isinstance(module, nn.Linear) and module.in_features == module.out_features:
            lora = LoRA(module.in_features, module.out_features, rank)
            setattr(module, 'lora', lora)
            original_forward = module.forward
            module.forward = lambda x, of=original_forward, l=lora: of(x) + l(x)

8.3 · YaRN-scaled RoPE Frequency Computation (from model_minimind.py)

def precompute_freqs_cis(dim, end, theta=1e6, scaling_config=None):
    freqs = 1.0 / (theta ** (torch.arange(0, dim, 2).float() / dim))
    if scaling_config is not None:
        # YaRN: f'(i) = f(i) * ((1 - gamma) + gamma / scale)
        factor    = scaling_config["factor"]        # 16
        beta_fast = scaling_config["beta_fast"]      # 32
        beta_slow = scaling_config["beta_slow"]      # 1
        # Compute linear ramp gamma โˆˆ [0, 1]
        low  = math.floor(dim * math.log(end / (beta_fast * 2 * math.pi)) / (2 * math.log(theta)))
        high = math.ceil(dim * math.log(end / (beta_slow * 2 * math.pi)) / (2 * math.log(theta)))
        gamma = (torch.arange(0, dim // 2).float() - low) / (high - low)
        gamma = gamma.clamp(0, 1)
        freqs = freqs * ((1 - gamma) + gamma / factor)
    t = torch.arange(end, dtype=torch.float32)
    freqs = torch.outer(t, freqs)
    return torch.cos(freqs), torch.sin(freqs)