A simple, minimalistic, and explainable implementation of Nemotron 3 Nano in JAX/Flax NNX.
Nemotron 3 Nano is an efficient hybrid Mamba-Transformer model with Mixture-of-Experts (MoE), designed for agentic reasoning. This codebase prioritizes clarity and educational value over performance optimization, making it ideal for understanding how modern hybrid architectures work.
The model alternates between two types of mixer blocks:
Each mixer is followed by a Sparse Mixture-of-Experts (MoE) layer.
mamba_2.py)attention.py)moe.py)nemotron.py)patterns list (e.g., mamba_moe and mamba_attention_moe blocks)jax[cpu] or jax[cuda])# Clone or navigate to the project
cd nugie-jax-nemotron-3-nano
# Create and activate virtual environment (optional)
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install "jax[cpu]" flax optax orbax-checkpoint datasets transformers
Use the ready notebook at notebooks/pretrain_nemotron.ipynb.
Local Jupyter:
jupyter notebook notebooks/pretrain_nemotron.ipynb
Google Colab:
notebooks/pretrain_nemotron.ipynb.pretrained.py implements the full pretraining workflow:
nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 tokenizer from Hugging Face.HuggingFaceFW/fineweb-edu (no full download required).CHECKPOINT_EVERY steps; resumes from latest checkpoint automatically.python pretrained.py
Key hyperparameters are constants at the top of pretrained.py:
VOCAB_SIZE = 131072 # Nemotron tokenizer vocabulary size
SEQ_LEN = 256 # Tokens per training sample (must be divisible by CHUNK_SIZE)
CHUNK_SIZE = 64 # Mamba SSD chunk size
BATCH_SIZE = 2
LEARNING_RATE = 3e-4
CHECKPOINT_EVERY = 200 # Save a checkpoint every N steps
MAX_TRAIN_STEPS = 10000
WARMUP_STEPS = 1000 # Linear warmup for the first N steps
VAL_STEPS = 50 # Batches averaged for validation
MAX_GEN_TOKENS = 200 # Max new tokens per chat response
MAX_CTX_LEN = 512 # Rolling context window during generation
Model weights are saved using Orbax in checkpoints/. The training loop automatically resumes from the latest checkpoint if one exists:
checkpoints/
└── <step>/ # Orbax checkpoint directory per step
nugie-jax-nemotron-3-nano/
├── pretrained.py # Pretraining loop, evaluation, and interactive chat
├── nemotron.py # Main model architecture (config + hybrid layer blocks)
├── attention.py # Grouped-Query Attention (GQA) implementation
├── mamba_2.py # Mamba 2 State-Space Model blocks (SSD algorithm)
├── moe.py # Sparse Mixture-of-Experts implementation
├── notebooks/
│ └── pretrain_nemotron.ipynb # Jupyter / Google Colab notebook
├── checkpoints/ # Orbax checkpoint directories (created at runtime)
├── LICENSE # Apache 2.0
└── README.md # This file
Model architecture is configured via NemotronConfig. Three named presets are available through NemotronConfig.from_preset():
| Preset | d_model | Layers | Notes |
|---|---|---|---|
tiny (default) | 128 | 10 | Fits on any CPU; good for quick local tests |
kaggle / colab | 256 | 13 | Medium size; fits a Kaggle/Colab GPU |
paper_close | 2048 | 26 | Closest to the published Nemotron 3 Nano style |
from nemotron import NemotronConfig, NemotronNanoBlock
from flax import nnx
config = NemotronConfig.from_preset("tiny") # or "kaggle", "paper_close"
config.vocab_size = 131072 # match your tokenizer
model = NemotronNanoBlock(rngs=nnx.Rngs(0), config=config)
Full list of NemotronConfig fields:
NemotronConfig(
vocab_size=1000, # Vocabulary size (set from tokenizer)
d_model=128, # Embedding / hidden dimension
# Layer pattern: list of (block_type, repeats)
# block_type ∈ {"mamba_moe", "mamba_attention_moe"}
patterns=[("mamba_moe", 2), ("mamba_attention_moe", 1), ...],
# Attention (GQA)
num_attention_heads=4, # Query heads
num_kv_heads=1, # KV heads (num_attention_heads % num_kv_heads == 0)
attention_head_dim=32, # num_attention_heads * attention_head_dim == d_model
# Mamba-2 SSM
mamba_d_state=64, # SSM state dimension
mamba_d_conv=4, # Causal conv kernel width
mamba_expand=2, # Inner dim = mamba_expand * d_model
mamba_headdim=64, # Dimension per Mamba head
mamba_ngroups=1, # B/C groups (like GQA for Mamba)
mamba_chunk_size=64, # SSD chunk size (seq_len must be divisible)
# Sparse MoE
num_experts=4, # Routed (base) expert count
num_shared_experts=1, # Always-on shared experts
top_k=2, # Top-k routed experts per token
expert_hidden_dim=256, # Expert FFN hidden dimension
granularity_factor=1, # Splits each expert into finer sub-experts
scale_top_k_with_granularity=True, # Scale top_k by granularity_factor
rms_norm_eps=1e-6, # RMSNorm epsilon
)
NemotronConfig.validate() checks all shape constraints (e.g., d_model == num_attention_heads * attention_head_dim) and raises an AssertionError with a descriptive message if any constraint is violated.
This implementation is inspired by:
Nemotron 3 Nano Paper: "Nemotron 3 Nano: Open, Efficient Mixture-of-Experts Hybrid Mamba-Transformer Model for Agentic Reasoning"
arXiv:2512.20848
Mamba 2 / SSD: "Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality" (Dao & Gu, 2024)
arXiv:2405.21060
Mamba: "Mamba: Linear-Time Sequence Modeling with Selective State Spaces"
arXiv:2312.08636
Attention Is All You Need: "Attention Is All You Need"
arXiv:1706.03762
MoE Designs: "DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models"
arXiv:2401.06066
Apache License 2.0 – See LICENSE for details.
This is primarily an educational project. Feel free to:
In Progress – Core architecture is implemented and functional. Ongoing work includes:
tiny preset to verify correctness locally before scaling upmamba_attention_moe blocks for mamba_moe to measure attention's contributionSEQ_LEN and MAX_CTX_LEN must both be divisible by CHUNK_SIZEQuestions or suggestions? Refer to inline code comments for detailed explanations of each component.
109 commits
Python
56.0%
Jupyter Notebook
44.0%
A simple, minimalistic, and explainable implementation of Nemotron 3 Nano in JAX/Flax NNX.
Nemotron 3 Nano is an efficient hybrid Mamba-Transformer model with Mixture-of-Experts (MoE), designed for agentic reasoning. This codebase prioritizes clarity and educational value over performance optimization, making it ideal for understanding how modern hybrid architectures work.
The model alternates between two types of mixer blocks:
Each mixer is followed by a Sparse Mixture-of-Experts (MoE) layer.
mamba_2.py)attention.py)moe.py)nemotron.py)patterns list (e.g., mamba_moe and mamba_attention_moe blocks)jax[cpu] or jax[cuda])# Clone or navigate to the project
cd nugie-jax-nemotron-3-nano
# Create and activate virtual environment (optional)
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install "jax[cpu]" flax optax orbax-checkpoint datasets transformers
Use the ready notebook at notebooks/pretrain_nemotron.ipynb.
Local Jupyter:
jupyter notebook notebooks/pretrain_nemotron.ipynb
Google Colab:
notebooks/pretrain_nemotron.ipynb.pretrained.py implements the full pretraining workflow:
nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 tokenizer from Hugging Face.HuggingFaceFW/fineweb-edu (no full download required).CHECKPOINT_EVERY steps; resumes from latest checkpoint automatically.python pretrained.py
Key hyperparameters are constants at the top of pretrained.py:
VOCAB_SIZE = 131072 # Nemotron tokenizer vocabulary size
SEQ_LEN = 256 # Tokens per training sample (must be divisible by CHUNK_SIZE)
CHUNK_SIZE = 64 # Mamba SSD chunk size
BATCH_SIZE = 2
LEARNING_RATE = 3e-4
CHECKPOINT_EVERY = 200 # Save a checkpoint every N steps
MAX_TRAIN_STEPS = 10000
WARMUP_STEPS = 1000 # Linear warmup for the first N steps
VAL_STEPS = 50 # Batches averaged for validation
MAX_GEN_TOKENS = 200 # Max new tokens per chat response
MAX_CTX_LEN = 512 # Rolling context window during generation
Model weights are saved using Orbax in checkpoints/. The training loop automatically resumes from the latest checkpoint if one exists:
checkpoints/
└── <step>/ # Orbax checkpoint directory per step
nugie-jax-nemotron-3-nano/
├── pretrained.py # Pretraining loop, evaluation, and interactive chat
├── nemotron.py # Main model architecture (config + hybrid layer blocks)
├── attention.py # Grouped-Query Attention (GQA) implementation
├── mamba_2.py # Mamba 2 State-Space Model blocks (SSD algorithm)
├── moe.py # Sparse Mixture-of-Experts implementation
├── notebooks/
│ └── pretrain_nemotron.ipynb # Jupyter / Google Colab notebook
├── checkpoints/ # Orbax checkpoint directories (created at runtime)
├── LICENSE # Apache 2.0
└── README.md # This file
Model architecture is configured via NemotronConfig. Three named presets are available through NemotronConfig.from_preset():
| Preset | d_model | Layers | Notes |
|---|---|---|---|
tiny (default) | 128 | 10 | Fits on any CPU; good for quick local tests |
kaggle / colab | 256 | 13 | Medium size; fits a Kaggle/Colab GPU |
paper_close | 2048 | 26 | Closest to the published Nemotron 3 Nano style |
from nemotron import NemotronConfig, NemotronNanoBlock
from flax import nnx
config = NemotronConfig.from_preset("tiny") # or "kaggle", "paper_close"
config.vocab_size = 131072 # match your tokenizer
model = NemotronNanoBlock(rngs=nnx.Rngs(0), config=config)
Full list of NemotronConfig fields:
NemotronConfig(
vocab_size=1000, # Vocabulary size (set from tokenizer)
d_model=128, # Embedding / hidden dimension
# Layer pattern: list of (block_type, repeats)
# block_type ∈ {"mamba_moe", "mamba_attention_moe"}
patterns=[("mamba_moe", 2), ("mamba_attention_moe", 1), ...],
# Attention (GQA)
num_attention_heads=4, # Query heads
num_kv_heads=1, # KV heads (num_attention_heads % num_kv_heads == 0)
attention_head_dim=32, # num_attention_heads * attention_head_dim == d_model
# Mamba-2 SSM
mamba_d_state=64, # SSM state dimension
mamba_d_conv=4, # Causal conv kernel width
mamba_expand=2, # Inner dim = mamba_expand * d_model
mamba_headdim=64, # Dimension per Mamba head
mamba_ngroups=1, # B/C groups (like GQA for Mamba)
mamba_chunk_size=64, # SSD chunk size (seq_len must be divisible)
# Sparse MoE
num_experts=4, # Routed (base) expert count
num_shared_experts=1, # Always-on shared experts
top_k=2, # Top-k routed experts per token
expert_hidden_dim=256, # Expert FFN hidden dimension
granularity_factor=1, # Splits each expert into finer sub-experts
scale_top_k_with_granularity=True, # Scale top_k by granularity_factor
rms_norm_eps=1e-6, # RMSNorm epsilon
)
NemotronConfig.validate() checks all shape constraints (e.g., d_model == num_attention_heads * attention_head_dim) and raises an AssertionError with a descriptive message if any constraint is violated.
This implementation is inspired by:
Nemotron 3 Nano Paper: "Nemotron 3 Nano: Open, Efficient Mixture-of-Experts Hybrid Mamba-Transformer Model for Agentic Reasoning"
arXiv:2512.20848
Mamba 2 / SSD: "Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality" (Dao & Gu, 2024)
arXiv:2405.21060
Mamba: "Mamba: Linear-Time Sequence Modeling with Selective State Spaces"
arXiv:2312.08636
Attention Is All You Need: "Attention Is All You Need"
arXiv:1706.03762
MoE Designs: "DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models"
arXiv:2401.06066
Apache License 2.0 – See LICENSE for details.
This is primarily an educational project. Feel free to:
In Progress – Core architecture is implemented and functional. Ongoing work includes:
tiny preset to verify correctness locally before scaling upmamba_attention_moe blocks for mamba_moe to measure attention's contributionSEQ_LEN and MAX_CTX_LEN must both be divisible by CHUNK_SIZEQuestions or suggestions? Refer to inline code comments for detailed explanations of each component.
109 commits
Python
56.0%
Jupyter Notebook
44.0%