nanowhale: HuggingFace 实习生从零实现的微型 DeepSeek-V4

> 一句话版本: HuggingFace 给实习生安排了一个任务 —— 从零复现 DeepSeek-V4 的全部创新架构压缩到 110M 参数,在单张 H100 上训 5K 步就开源出来了。等于有人帮你把 DeepSeek-V4 几十页技术论文拆成了 500 行 PyTorch 代码。

背景故事

ml-intern 是 HuggingFace 的内部 AI 导师项目,给实习生分配工程任务来学大模型实战。

这一次,他们给实习生 cmpatino 的任务是:

> 用 DeepSeek-V4 的全部架构创新训练一个微型 MoE 模型。

结果就是 nanowhale 🐳 —— 从零实现的 DeepSeek-V4,不依赖任何 DeepSeek 的预训练权重,只借用了它们的 tokenizer。

架构:DeepSeek-V4 的迷你复刻

相比于用 Llama/RWKV 等成熟架构的教学模型,nanowhale 的特殊之处在于它完整实现了 DeepSeek-V4 的四大核心创新:

MLA(Multi-head Latent Attention)

DeepSeek-V4 的核心注意力机制——通过低秩投影(lora_rank=160)压缩 KV cache。在这个 110M 模型里:

这是 MLA 首次以如此清晰的迷你形式在开源代码中出现。

MoE(Mixture of Experts)

Hyper-Connections(替代残差连接)

DeepSeek-V4 用 Hyper-Connections 替代了标准的残差连接:

MTP(Multi-Token Prediction)

训练细节

阶段数据集步数Token 数精度
**预训练**FineWeb-Edu5,000~2.6Bbf16
**SFT**SmolTalk (46 万对话)3,000~72.7Mfp32 ❗

硬件:单卡 NVIDIA H100 80GB(估算 H100 成本 ~$3/h × 约 15h = ~$50)

指标预训练后SFT 后
验证困惑度13.6212.90
Token 准确率33.8%48.5%
训练 loss~5.32.607(eval)

已知问题(非常诚实)

模型卡直接标注了三项已知限制:

1. bf16 NaN:Hyper-Connections 在这个小尺度下值溢出 —— 必须用 fp32 推理和训练。 DeepSeek 原版在 671B 参数时这个架构没问题,但缩小到 110M 后数值稳定性变了。这里有实用的教育价值:不是所有架构都能线性缩放到小尺寸。

2. 词表过大:129K 的 DeepSeek-V4 tokenizer 导致 37% 的参数(41M/110M)花在 embedding 上,留给实际语言建模能力的只剩 69M。这个 129K tokenizer 对 7B 模型合适,对 110M 是负担。

3. from_pretrained 奇怪行为:自定义架构导致 from_pretrained 会重新初始化部分权重 —— 官方推荐的加载方式是 load_state_dict

代码库结构


nanowhale/
├── modeling_deepseek_v4.py   ← 核心模型定义(~500 行)
├── configuration_deepseek_v4.py
├── scripts/
│   ├── train_pretrain.py     # 预训练脚本
│   ├── train_sft.py          # SFT 微调
│   ├── chat.py               # 交互式聊天
│   ├── eval_smoke.py         # 评估
│   └── upload_to_hub.py      # 上传到 HuggingFace
└── tokenizer/
    ├── tokenizer.json         # DeepSeek-V4 tokenizer
    └── tokenizer_config.json

与其他教学模型的对比

模型参数架构用途训练成本
**nanoGPT**~0.1B标准 TransformerGPT-2 教学极低
**nanowhale****0.11B****DeepSeek-V4** (MLA+MoE+HC+MTP)**前沿架构教学**~$50
**nanoChat**~0.1B标准 TransformerChat 教学极低
TinyLlama1.1BLlama实际可用~$500
SmolLM21.7BLlama实际可用~$1K

实际跑一下


from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file

config = AutoConfig.from_pretrained("HuggingFaceTB/nanowhale-100m-base", trust_remote_code=True)
model = AutoModelForCausalLM.from_config(config, trust_remote_code=True).float()

weights_path = hf_hub_download("HuggingFaceTB/nanowhale-100m-base", "model.safetensors")
model.load_state_dict(load_file(weights_path), strict=True)

tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/nanowhale-100m-base")

⚠️ 注意:必须 trust_remote_code=True,且推荐用 load_state_dict 而非 from_pretrained

评分

维度评分说明
教学价值★★★★★目前唯一公开的 DeepSeek-V4 迷你实现,500 行代码搞定了 MLA+MoE+HC+MTP
代码质量★★★★★HuggingFace 工程水准,结构清晰
实用性★★☆☆☆110M 模型生成质量很低,纯粹教育用途
文档诚实度★★★★★已知问题写得非常清楚,包括为什么 from_pretrained 会坏
独特价值★★★★★填补了"想学 DeepSeek-V4 但看不懂 50 页论文"的空缺

一句话总结

> HuggingFace 实习生从零复刻了 DeepSeek-V4 的全部架构创新(MLA / Hyper-Connections / MoE / MTP),压缩到 110M 参数跑通 —— 想学 DeepSeek-V4 架构的话,这是目前最好的入门代码。