Instructions to use Akahsizrr/Mini-Whale-1-12B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Akahsizrr/Mini-Whale-1-12B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="Akahsizrr/Mini-Whale-1-12B", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("Akahsizrr/Mini-Whale-1-12B", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Akahsizrr/Mini-Whale-1-12B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Akahsizrr/Mini-Whale-1-12B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akahsizrr/Mini-Whale-1-12B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Akahsizrr/Mini-Whale-1-12B
- SGLang
How to use Akahsizrr/Mini-Whale-1-12B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Akahsizrr/Mini-Whale-1-12B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akahsizrr/Mini-Whale-1-12B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Akahsizrr/Mini-Whale-1-12B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Akahsizrr/Mini-Whale-1-12B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use Akahsizrr/Mini-Whale-1-12B with Docker Model Runner:
docker model run hf.co/Akahsizrr/Mini-Whale-1-12B
Mini Whale 1 12B
A 12B-parameter Mixture-of-Experts model that fuses Qwen3-4B (host) with 260 coding experts extracted from DeepSeek-V4-Flash, fine-tuned with QLoRA and optimized for local inference on consumer GPUs (RTX 3060 12GB).
Table of Contents
- What is this model?
- Architecture (A to Z)
- Training
- Local Inference Guide
- DSpark Speculative Decoding
- Performance
- File Structure
- Citation
What is this model?
Mini Whale 1 12B is a fusion architecture that combines:
- Host model: Qwen3-4B (36 layers, hidden dim 2560, BF16) — handles language understanding, reasoning, and general text generation
- Coding experts: 260 SwiGLU experts from DeepSeek-V4-Flash (each ~25M params, hidden dim 4096) — specialized for code generation
- Bridge layers: Low-rank adapters (rank 7) that translate between Qwen3's 2560-dim representation space and DeepSeek's 4096-dim expert space
- Router: sqrt-softplus scoring with top-2 expert selection per token
The result is a model that thinks like Qwen3 but codes like DeepSeek — all while fitting in 12GB VRAM with 4-bit quantization.
Architecture (A to Z)
Overview
Input Tokens
│
▼
┌─────────────────────────────────────────┐
│ Qwen3-4B Host (36 layers, hidden=2560) │
│ │
│ For each layer: │
│ 1. Self-Attention (SDPA, 32 heads) │
│ 2. Host MLP (SwiGLU, 9728 intermediate)│
│ 3. [Augmented layers only]: │
│ a. Bridge In (2560 → 4096) │
│ b. Router (sqrt-softplus, top-2) │
│ c. Expert SwiGLU (2 of N experts) │
│ d. Bridge Out (4096 → 2560) │
│ e. RMSNorm + Sigmoid Gate │
│ f. Residual Repair (rank-7) │
│ g. Residual Addition │
│ 4. RMSNorm │
└─────────────────────────────────────────┘
│
▼
LM Head (tied embeddings, 151936 vocab)
│
▼
Output Logits → Token
Layer Structure (36 layers)
All 36 layers are augmented (have coding experts), but the number of experts per layer varies:
| Layers | Expert Count | Description |
|---|---|---|
| 0-8 | 1-8 | Early layers: fewer experts, basic coding patterns |
| 9 | 1 | Minimal augmentation (dense-like) |
| 10-28 | 2-13 | Middle layers: moderate expert density |
| 29-35 | 8-25 | Deep layers: heavy expert density for complex code |
Total experts across all layers: 303 expert instances (260 unique experts, some shared across layers).
Expert Computation
Each expert is a standard SwiGLU MLP:
expert_output = down_proj(
clamp(silu(gate_proj(x)) * up_proj(x), -10.0, 10.0)
)
The clamp(-10, 10) is critical — it matches DeepSeek-V4-Flash's native swiglu_limit and prevents activation explosion across layers.
Bridge Layers
The bridge is the key innovation. It translates between two different representation spaces:
- bridge_in:
Linear(2560, 4096)— maps Qwen3 hidden states to DeepSeek expert space - bridge_out:
Linear(4096, 2560)— maps expert outputs back to Qwen3 space
Both are low-rank (rank 7) to keep parameter count low and prevent the experts from overwhelming the host signal.
Router
logits = router.gate(hidden_states) # (tokens, num_experts)
scores = sqrt(clamp(softplus(logits), min=1e-6)) # sqrt-softplus scoring
topk_weights, topk_indices = scores.topk(2, dim=-1) # top-2 selection
topk_weights = topk_weights / topk_weights.sum(dim=-1) # normalize
The sqrt-softplus scoring is smoother than softmax and prevents expert collapse (one expert dominating all tokens).
Residual Safety
The coding delta is gated and clamped to prevent it from disrupting the host's representation:
gate = sigmoid(coding_gate) # learnable scalar, init -2.0 → ~12% gate
coding_delta = bridge_out(expert_output)
coding_delta = coding_norm(coding_delta)
coding_delta = coding_delta * gate
# Clamp to max_delta_ratio of host signal norm
scale = min(1.0, (host_norm * max_delta_ratio) / delta_norm)
coding_delta = coding_delta * scale
# Repair (rank-7 residual correction)
repair_delta = repair_up(repair_down(host_hidden))
# Final residual
output = host_hidden + coding_delta + repair_delta
This ensures the coding experts contribute to the output without dominating it. The host model's reasoning ability is preserved.
Key Config
| Parameter | Value |
|---|---|
| Host hidden size | 2560 |
| Host intermediate size | 9728 |
| Host layers | 36 |
| Host attention heads | 32 (Q) / 8 (KV) |
| Head dim | 128 |
| Expert hidden size | 4096 |
| Expert intermediate size | 2048 |
| Top-k experts | 2 |
| Bridge rank | 7 |
| Vocab size | 151,936 |
| Max position | 40,960 |
| RoPE theta | 1,000,000 |
| Total params | ~12B |
| Quantized size (4-bit) | 23.4 GB (BF16) / 8.9 GB (4-bit) |
Training
Stage 1: Bridge + Router Training (QLoRA)
- Method: QLoRA (4-bit NF4 host + trainable LoRA adapters on attention + bridge layers)
- Trainable params: 19.5M (bridge_in, bridge_out, router.gate, repair, q/k/v/o_proj LoRA)
- Dataset: Coding instruction dataset (Python, JavaScript, TypeScript)
- Steps: 500
- Loss: 0.62 → 0.19
- Optimizer: AdamW, lr=2e-4, cosine schedule
- Hardware: RTX 3060 12GB (local)
Stage 2: Merge
LoRA adapters were merged into the base model using streaming tensor-by-tensor merge:
- 216 LoRA modules merged (B = A @ B for each adapter)
- 144 checkpoint overrides applied
- 1285 tensors copied unchanged
- Output: 23.4 GB BF16 model (5 shards)
Critical Runtime Fixes
Two fixes are required at inference time (applied automatically by the model code):
SwiGLU clamping (
limit=10.0): DeepSeek-V4-Flash usesswiglu_limit=10.0to clamp expert intermediate activations. Without this, outlier values grow exponentially across layers.Router stability:
softplusis clamped tomin=1e-6beforesqrt()to prevent NaN gradients.
Local Inference Guide
Requirements
| Component | Minimum | Recommended |
|---|---|---|
| GPU | RTX 3060 12GB | RTX 4070 16GB+ |
| RAM | 16GB | 32GB |
| Python | 3.10+ | 3.11+ |
| CUDA | 12.1+ | 12.6+ |
| VRAM (4-bit) | 8.9 GB | — |
| VRAM (4-bit + DSpark) | 10.9 GB | — |
Installation
# Create virtual environment
python -m venv fuse2-venv
fuse2-venv\Scripts\activate
# Install dependencies
pip install torch --index-url https://download.pytorch.org/whl/cu126
pip install transformers==5.14.1 bitsandbytes==0.49 peft==0.20 accelerate
pip install safetensors
Download the Model
pip install huggingface_hub
huggingface-cli download Akahsizrr/Mini-Whale-1-12B --local-dir ./mini-whale-1-12b
Quick Start: One-Liner 4-bit Loading (~5 tok/s)
The model has all runtime fixes baked into the model code (fuse2_model.py):
- SwiGLU clamping (limit=10.0) — built into
SwiGLUExpert.forward - Router stability (softplus clamp) — built into
Fuse2Router.forward - Meta param auto-init + norm dtype fix — handled by
from_pretrainedoverride
So you can load and generate with zero post-load patching:
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
MODEL = "Akahsizrr/Mini-Whale-1-12B"
# 4-bit NF4 quantization (matches config.json quantization_config)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
)
# Load model — runtime fixes applied automatically by from_pretrained override
model = AutoModelForCausalLM.from_pretrained(
MODEL,
trust_remote_code=True,
quantization_config=bnb_config,
device_map="auto",
attn_implementation="sdpa",
)
model.eval()
# Tokenizer
tok = AutoTokenizer.from_pretrained(MODEL, trust_remote_code=True)
# Generate
messages = [{"role": "user", "content": "Write a Python fizzbuzz."}]
text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
input_ids = tok(text, return_tensors="pt").input_ids.to(model.device)
with torch.inference_mode():
out = model.generate(input_ids, max_new_tokens=512, do_sample=False,
repetition_penalty=1.3)
print(tok.decode(out[0], skip_special_tokens=True))
That's it — no manual SwiGLU patching, no meta device fixes, no norm dtype casting. The model code handles all of it.
Quick Start: Manual 4-bit Loading (~5 tok/s)
For more control over the loading process (e.g., selective quantization of attention vs MLP layers):
import torch, json, os, sys, gc
from transformers import AutoConfig, AutoTokenizer
from accelerate import init_empty_weights
from safetensors import safe_open
import bitsandbytes as bnb
MODEL_PATH = "./mini-whale-1-12b"
DEVICE = "cuda:0"
sys.path.insert(0, MODEL_PATH)
import fuse2_model_local
# Load config with SDPA attention
config = AutoConfig.from_pretrained(MODEL_PATH, trust_remote_code=True)
config._attn_implementation = "sdpa"
# Create model on meta device (saves RAM)
with init_empty_weights():
model = fuse2_model_local.Fuse2ForCausalLM(config)
# Load weights with 4-bit quantization
quant_suffixes = ("q_proj.weight", "k_proj.weight", "v_proj.weight", "o_proj.weight",
"gate_proj.weight", "up_proj.weight", "down_proj.weight")
with open(f"{MODEL_PATH}/model.safetensors.index.json") as f:
index = json.load(f)
weight_map = index["weight_map"]
def nav(model, key):
parts = key.split(".")
obj = model
for p in parts[:-1]:
obj = obj[int(p)] if p.isdigit() else getattr(obj, p)
return obj, parts[-1]
def find_linear(model, key):
parts = key.split(".")
obj = model
for p in parts[:-2]:
obj = obj[int(p)] if p.isdigit() else getattr(obj, p)
return obj, parts[-2]
param_names = set(dict(model.named_parameters()).keys())
replaced = {}
for shard_name in sorted(set(weight_map.values())):
with safe_open(os.path.join(MODEL_PATH, shard_name), framework="pt", device="cpu") as f:
for key in [k for k, v in weight_map.items() if v == shard_name]:
if key not in param_names:
continue
tensor = f.get_tensor(key)
if any(key.endswith(s) for s in quant_suffixes):
owner, attr = find_linear(model, key)
old = getattr(owner, attr)
new = bnb.nn.Linear4bit(old.in_features, old.out_features,
bias=False, quant_type="nf4",
compute_dtype=torch.bfloat16, device=DEVICE)
new.weight = bnb.nn.Params4bit(tensor.to(torch.bfloat16),
requires_grad=False, quant_type="nf4").cuda(0)
setattr(owner, attr, new)
else:
parent, pname = nav(model, key)
parent._parameters[pname] = torch.nn.Parameter(
tensor.to(torch.bfloat16).to(DEVICE), requires_grad=False)
del tensor
gc.collect(); torch.cuda.empty_cache()
# Fix tied embeddings
if model.lm_head.weight.device.type == 'meta':
model.lm_head.weight = torch.nn.Parameter(
model.model.embed_tokens.weight.data.clone(), requires_grad=False)
# Apply runtime fixes (SwiGLU clamp + router stability)
from fuse2_model_local import Fuse2AugmentedLayer
import torch.nn.functional as F
for layer in model.model.layers:
if not isinstance(layer, Fuse2AugmentedLayer):
continue
if hasattr(layer, 'coding_gate') and layer.coding_gate.device.type == 'meta':
layer.coding_gate = torch.nn.Parameter(torch.tensor(-2.0, device=DEVICE))
if hasattr(layer, 'coding_norm') and layer.coding_norm.weight.device.type == 'meta':
layer.coding_norm = torch.nn.RMSNorm(
layer.coding_norm.weight.shape[0], eps=1e-6).to(DEVICE)
experts = getattr(layer, "experts", None)
if experts:
for expert in experts:
gp, up, dp = expert.gate_proj, expert.up_proj, expert.down_proj
def make_fwd(g, u, d, lim=10.0):
def forward(x):
return d(torch.clamp(F.silu(g(x)) * u(x), -lim, lim))
return forward
expert.forward = make_fwd(gp, up, dp)
router = getattr(layer, "router", None)
if router:
gate, top_k = router.gate, router.top_k
def make_router(g, tk):
def forward(h):
logits = g(h)
scores = torch.clamp(F.softplus(logits), min=1e-6).sqrt()
w, idx = scores.topk(tk, dim=-1)
return w / (w.sum(dim=-1, keepdim=True) + 1e-8), idx, logits
return forward
router.forward = make_router(gate, top_k)
model.set_coding_enabled(True)
model.to(DEVICE)
model.eval()
# Fix norm dtypes (float32 → bfloat16 for fused kernels)
for module in model.modules():
if hasattr(module, 'weight') and hasattr(module, 'eps'):
if module.weight.dtype == torch.float32:
module.weight.data = module.weight.data.to(torch.bfloat16)
# Tokenizer
tok = AutoTokenizer.from_pretrained(MODEL_PATH, trust_remote_code=True)
# Generate
messages = [{"role": "user", "content": "Write a Python fizzbuzz."}]
text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
input_ids = tok(text, return_tensors="pt").input_ids.to(DEVICE)
with torch.inference_mode():
out = model.generate(input_ids, max_new_tokens=512, do_sample=False,
repetition_penalty=1.3)
print(tok.decode(out[0], skip_special_tokens=True))
DSpark Speculative Decoding (~10 tok/s)
For 2x speedup, use DSpark speculative decoding. This requires the drafter model:
# The drafter is included in the model directory
# It's a 5-layer Qwen3 model that predicts 7 tokens at once
See the fuse2_dspark_fast.py script (included in this repo) for the full implementation.
VRAM Breakdown
| Configuration | VRAM (allocated) | VRAM (reserved) | Speed |
|---|---|---|---|
| 4-bit target only | 8.9 GB | 9.4 GB | ~5 tok/s |
| 4-bit target + BF16 drafter | 11.7 GB | 12.2 GB | OVERFLOW |
| 4-bit target + 4-bit drafter | 10.3 GB | 10.8 GB | ~8 tok/s |
| BF16-attn target + 4-bit drafter (shared embed) | 10.9 GB | 11.4 GB | ~10 tok/s |
Critical: On RTX 3060 12GB, Windows reserves ~0.5GB for display. If reserved VRAM exceeds ~11.5GB, CUDA silently spills to system RAM via PCIe, causing a 50x slowdown. Always check torch.cuda.memory_reserved(0).
DSpark Speculative Decoding
This model supports DSpark (Draft Speculative) decoding for 2x speedup:
How it works
- Drafter (5-layer Qwen3, 2560 hidden): Predicts 7 tokens in a single forward pass using masked attention + target hidden states
- Target (12B Fuse-2): Verifies all 7 tokens in ONE forward pass
- Greedy verification: Accept the longest prefix where drafter matches target's argmax
- Bonus token: Target always produces 1 bonus token (even if all 7 are rejected)
Key optimizations
- Forward hooks on 5 target layers (not
output_hidden_states=Trueon all 36) - Single target forward per block (no sequential fallback)
- 4-bit drafter to save VRAM
- Shared embedding between target and drafter (saves 0.8 GB)
- Sliding window KV cache (512 tokens) for constant speed on long sequences
Acceptance rate
- Short prompts: 3.0-3.4/7 (43-49%)
- Long prompts: 2.3-2.5/7 (33-36%)
- Code patterns: up to 4.5/7 (64%)
Performance
Speed (RTX 3060 12GB, 4-bit NF4)
| Mode | Speed (short) | Speed (long, 512+ tok) | VRAM |
|---|---|---|---|
| Basic generation | 5.5 tok/s | 5.5 tok/s | 8.9 GB |
| DSpark speculative | 10.0 tok/s | 8.5 tok/s | 10.9 GB |
Quality
The model produces:
- Reasoning: Qwen3-quality chain-of-thought (the host handles this)
- Code: DeepSeek-quality code generation (the experts handle this)
- Mixed: Seamless switching between reasoning and code
Example output
Prompt: "Write a Python function to check if a number is prime."
Output (excerpt):
Okay, I need to write a Python function to check if a number is prime.
Let me think about how to approach this.
First, a prime number is a number greater than 1 that has no divisors
other than 1 and itself...
1. Check if the number is less than 2 → return False.
2. Check if the number is 2 → return True
3. Check if the number is even → if yes, return False
4. Iterate from 3 to sqrt(n), stepping by 2
5. For each i, check if n is divisible by i
6. If none divide n, return True
File Structure
mini-whale-1-12b/
├── config.json # Model configuration
├── generation_config.json # Generation defaults
├── model-00001-of-00005.safetensors # Shard 1 (5.1 GB)
├── model-00002-of-00005.safetensors # Shard 2 (5.1 GB)
├── model-00003-of-00005.safetensors # Shard 3 (5.1 GB)
├── model-00004-of-00005.safetensors # Shard 4 (5.1 GB)
├── model-00005-of-00005.safetensors # Shard 5 (3.5 GB)
├── model.safetensors.index.json # Shard index
├── fuse2_model.py # Model code (HF auto_map)
├── fuse2_model_local.py # Model code (local import)
├── tokenizer.json # Tokenizer
├── tokenizer_config.json # Tokenizer config
├── vocab.json # Vocabulary
├── merges.txt # BPE merges
└── README.md # This file
Citation
@misc{miniwhale1,
title={Mini Whale 1 12B: Fusing Qwen3-4B with DeepSeek Coding Experts},
author={Vasko Djack},
year={2026},
url={https://huggingface.co/Akahsizrr/Mini-Whale-1-12B}
}
License
Apache 2.0 — see LICENSE file for details.
Acknowledgments
- Qwen3-4B by Alibaba/Qwen Team — host model
- DeepSeek-V4-Flash by DeepSeek AI — coding experts
- DSpark speculative decoding framework
- bitsandbytes for 4-bit NF4 quantization
- Downloads last month
- -