2025年1月27日 作者:Daniel & Michael
运行 DeepSeek R1 动态 1.58-bit 量化 (原文链接)
DeepSeek-R1 近期引起了广泛关注,因为它在推理能力上可以媲美 OpenAI 的 O1 模型,同时仍然保持完全开源。我们研究了如何让更多本地用户能够运行它,并成功将 DeepSeek R1 这一拥有 6710 亿参数 的模型从 720GB 量化至 131GB,在保持良好功能性的同时,实现了 80% 的大小缩减。
在研究 DeepSeek R1 的架构后,我们采用了一种 动态量化 方法,对某些特定层进行 更高比特(如 4-bit) 量化,同时将大部分 MoE(专家混合) 层(类似于 GPT-4 使用的架构)量化至 1.5-bit。直接对所有层进行低比特量化会导致模型彻底崩溃,输出乱码或陷入无限循环,而我们的动态量化方法有效解决了这一问题。
如何运行 DeepSeek R1
详细的运行指南请参考:Guide to Run R1。
- 1.58-bit 量化模型 运行需要 160GB VRAM(2× H100 80GB 显卡),可实现 每秒 140 tokens 的吞吐量,并且在单用户推理时速度约为 每秒 14 tokens。
- 无需 GPU 运行:如果没有 GPU,仅使用 20GB RAM 也可以运行,但速度较慢。
- 推荐配置:为获得最佳性能,VRAM + RAM 总和至少需达到 80GB 以上。
量化模型下载
我们已将不同版本的 动态量化 DeepSeek R1 模型(大小范围 131GB - 212GB)上传至 Hugging Face:
🔗 huggingface.co/unsloth/DeepSeek-R1-GGUF
如果你喜欢我们的工作,欢迎给我们 Star ⭐:
🔗 github.com/unslothai/unsloth
或在 X(Twitter) 关注我们:@UnslothAi 💖
🦥 1. 动态量化版本
我们提供 4 个动态量化版本。前 3 个版本 使用 重要性矩阵(imatrix,基于 llama.cpp) 进行量化校准,从而允许更低比特表示。最后 212GB 版本是 2-bit 量化,但 未进行校准。
MoE Bits | 磁盘大小 | 类型 | 质量 | 量化方式 | 下载链接 |
1.58-bit | 131GB | IQ1_S | 一般 | IQ1_S(1.58bit) | 下载 |
1.73-bit | 158GB | IQ1_M | 良好 | IQ1_M(2.06/1.56bit) | 下载 |
2.22-bit | 183GB | IQ2_XXS | 更佳 | IQ2_XXS(2.5/2.06bit) | 下载 |
2.51-bit | 212GB | Q2_K_XL | 最佳 | Q2_K_XL(3.5/2.5bit) | 下载 |
💾 完整 R1 量化模型合集(包括 4-bit 版本、蒸馏版等) 👉 Hugging Face Collection
📊 2. 基准测试与消融实验
为了测试所有量化版本的性能,我们 不依赖通用基准测试,而是让 DeepSeek R1 生成一款 Flappy Bird 游戏(使用 Pass@3,即三次尝试)。我们根据 10 个标准评分,例如:
- 是否使用了 随机颜色
- 是否使用了 随机形状
- 代码是否能够在 Python 解释器中正常运行
对比:DeepSeek R1 原版 vs. 1.58bit 版本
左侧是 chat.deepseek.com 生成的 原版 DeepSeek R1 代码,右侧是 1.58-bit 量化版本 的生成结果:
📌 结果分析:
✅ 1.58-bit 量化版仍然可以生成有效代码,尽管模型大小缩减了 80%!
❌ 如果不使用我们的动态 1.58-bit 量化,而是直接将所有层量化:
- 1.58-bit 全部量化(无动态优化) → 无限循环(Seed 3407: “Colours with dark Colours with dark Colours with dark Colours with dark”)
- 1.75-bit(149GB 全部量化) → 无限循环停止,但 生成全黑屏幕,完全错误
- 2.06-bit(175GB 全部量化) → 比 1.58-bit 还差,建议使用 2.22-bit(183GB) 版本
注意:
- 1.58-bit 动态量化 可能在 每 8000 个 token 中偶尔生成 1 个错误 token,建议使用
min_p = 0.1
或 0.05
进行修正。
🏆 量化模型性能评分(Flappy Bird Benchmark)
模型大小 | 动态量化(Pass@3 分数/10) | 普通量化(Pass@3 分数/10) |
131GB (1.58-bit) | 6.92 | 0(无效输出) |
158GB (1.73-bit) | 9.08 | 1.67 |
183GB (2.22-bit) | 9.17 | 6.17 |
175GB(2.06-bit 全量化) | — | 6.17(比 1.58-bit 还差) |
📌 总结:
- 1.58-bit 动态量化(131GB)得分 69.2%,2-bit 183GB 版本得分 91.7%
- 非动态量化(如 1.58-bit 直接量化)得分为 0,甚至 175GB 量化版本的表现也比 131GB 动态量化更差!
🔍 完整基准测试结果,请查看博客文章结尾!
🐋 3. 深入解析 DeepSeek R1 的架构
在我们之前对 DeepSeek V3 模型的分析中,我们发现 DeepSeek R1 被用于合成数据生成。回顾一下,DeepSeek R1 的前 3 层是完全密集(dense)的,而 不是 MoE(Mixture of Experts,专家混合)。
MoE 层的设计允许 增加模型参数量,但 不会增加 FLOPs(浮点计算量),因为 MoE 通过动态屏蔽大部分神经元(设置为 0),跳过了对零值的矩阵计算。因此,MoE “欺骗” 了深度学习的规模化定律,让模型在 计算开销不变的情况下,扩展参数规模。
👉 关于 MoE 及更先进的 Memory Layers(旨在超越 MoE 的方法),可查看这篇推文:x.com/danielhanchen/status/1868748998783517093
我们结合了 4 个关键技术:
- 4-bit 动态量化(Dynamic Quantization)
- 1.58-bit LLMs 论文的研究
- Llama.cpp 的 1.5-bit 量化方法
- Super Weights 论文的量化策略
基于这些研究,我们发现:
✅ 前 3 层 Dense 层 仅占 0.5% 的总权重,应使用 4-bit 或 6-bit 量化。
✅ MoE 层的共享专家(Shared Experts) 仅占 1.5% 的总权重,可使用 6-bit 量化。
✅ MLA 注意力模块 可使用 4-bit 或 6-bit,但其输出部分(占比 3%)应 保持高精度。
✅ Down_proj(下投影层) 对量化 最敏感,特别是在 Transformer 的前几层,不能直接量化。
✅ Embedding 层与 LM Head 层:分别用 4-bit 和 6-bit。
✅ MoE Router 和所有 LayerNorm 层:保持 32-bit 精度。
🔍 结果:约 88% 的参数是 MoE 权重,通过 1.58-bit 量化,模型大小大幅缩小!
💡 关键观察:为何 down_proj 层不能随意量化?
根据 Super Weights 论文 和 SwiGLU 结构,计算公式如下:
f(XW_{gate}) * (XW_{up}) * W_{down}
📌 解释:
up
和 gate
投影层进行 乘法运算,结果数值会 放大。
- down_proj 层需要对放大的结果进行缩放,如果 down_proj 被量化过低,会导致计算失真。
- 因此,我们应该 保留 Transformer 前几层的 down_proj 以更高比特量化(如 6-bit)。
📌 最终量化策略:
💬 聊天模板问题修复
所有 DeepSeek R1 版本(包括蒸馏版) 采用相同的聊天格式:
<|begin▁of▁sentence|><|User|>What is 1+1?<|Assistant|>It's 2.<|end▁of▁sentence|><|User|>Explain more!<|Assistant|>
- BOS(句子起始符) 被 强制添加,EOS(结束符)用于 分隔交互。
- 避免双重 BOS Token:
tokenizer.encode(..., add_special_tokens = False)
- llama.cpp/GGUF 进行推理时,必须跳过 BOS,因为它会自动添加。
👉 正确格式:
<|User|>What is 1+1?<|Assistant|>
📌 特殊 Token 重新映射(蒸馏版 Qwen & Llama):
Token | R1 | Distill Qwen | Distill Llama |
<think> | 128798 | 151648 | 128013 |
</think> | 128799 | 151649 | 128014 |
<|begin_of_sentence|> | 0 | 151646 | 128000 |
<|end_of_sentence|> | 1 | 151643 | 128001 |
<|User|> | 128803 | 151644 | 128011 |
<|Assistant|> | 128804 | 151645 | 128012 |
Padding token | 2 | 151654 | 128004 |
📌 不同模型的 Token 兼容性
Token | Qwen 2.5 32B Base | Llama 3.3 70B Instruct |
<think> | <|box_start|> | <|reserved_special_token_5|> |
</think> | <|box_end|> | <|reserved_special_token_6|> |
<|begin_of_sentence|> | <|object_ref_start|> | <|begin_of_text|> |
<|end_of_sentence|> | <|endoftext|> | <|end_of_text|> |
<|User|> | <|im_start|> | <|reserved_special_token_3|> |
<|Assistant|> | <|im_end|> | <|reserved_special_token_4|> |
Padding token | <|vision_pad|> | <|finetune_right_pad_id|> |
🚨 所有蒸馏版本(Qwen & Llama)和原版 R1 可能错误地将 Padding Token 设为 <|end_of_sentence|>
!
- 这会导致 无限生成循环,因为大多数框架会屏蔽 EOS 作为
-100
。
- 修正方案:
- Qwen:
<|vision_pad|>
- Llama:
<|finetune_right_pad_id|>
- R1:
<|▁pad▁|>
或 新添加的 <|PAD▁TOKEN|>
✅ 总结
- MoE 量化策略:
- 88% MoE 层 → 1.58-bit,模型大幅缩小!
- 高精度量化部分(Dense, Down_proj, MLA Attention)→ 4-bit & 6-bit。
- Tokenizer 修正:
- 避免 BOS 重复添加,避免 Padding Token 误设为 EOS,防止 无限生成问题。
- 代码已提交至 llama.cpp! 👉 github.com/unslothai/llama.cpp
📌 想要运行优化后的 DeepSeek R1 ?
👉 Hugging Face DeepSeek R1 量化模型
👉 Unsloth 量化代码仓库
🖥️ 运行动态量化的 DeepSeek R1
✅ 无需使用新的 llama.cpp
版本——任何可以运行 GGUF 的系统(如 Ollama、OpenWebUI、Transformers)均可运行动态量化的 DeepSeek R1。
📌 注意:如果 VRAM 或 RAM 不足,运行可能会 变慢,但仍然可以运行。
🚀 1. 使用 llama.cpp
直接运行
如果想直接使用 llama.cpp
,请按照以下步骤进行 构建,别忘了 启用 GPU 支持!
安装依赖
apt-get update
apt-get install build-essential cmake curl libcurl4-openssl-dev -y
编译 llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cmake llama.cpp -B llama.cpp/build \
-DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=ON -DLLAMA_CURL=ON
cmake --build llama.cpp/build --config Release -j --clean-first --target llama-quantize llama-cli llama-gguf-split
cp llama.cpp/build/bin/llama-* llama.cpp
📥 2. 下载 1.58-bit 量化版本
使用 Hugging Face 下载模型:
from huggingface_hub import snapshot_download
snapshot_download(
repo_id = "unsloth/DeepSeek-R1-GGUF",
local_dir = "DeepSeek-R1-GGUF",
allow_patterns = ["*UD-IQ1_S*"],
)
📌 下载后,目录 DeepSeek-R1-GGUF/DeepSeek-R1-UD-IQ1_S/
内含 3 个 GGUF 文件。
📊 3. 计算 GPU 层数
如果 无 GPU,请将 --n-gpu-layers
设为 0
。
💡 计算公式:
n_{\text{offload}} = \frac{\text{VRAM (GB)}}{\text{文件大小 (GB)}} \times n_{\text{layers}} - 4
📌 DeepSeek R1 共有 61 层,以下是不同显存大小对应的 offload 层数:
量化版本 | 文件大小 | 24GB GPU | 80GB GPU | 2×80GB GPU |
1.58bit | 131GB | 7 | 33 | 61 (全部) |
1.73bit | 158GB | 5 | 26 | 57 |
2.22bit | 183GB | 4 | 22 | 49 |
2.51bit | 212GB | 2 | 19 | 32 |
🏃 4. 运行 DeepSeek R1
📌 RTX 4090(24GB VRAM)示例:
./llama.cpp/llama-cli \
--model DeepSeek-R1-GGUF/DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
--cache-type-k q4_0 \
--threads 16 \
--prio 2 \
--temp 0.6 \
--ctx-size 8192 \
--seed 3407 \
--n-gpu-layers 7 \
-no-cnv \
--prompt "<|User|>Create a Flappy Bird game in Python.<|Assistant|>"
📌 参数说明:
--threads 16
:使用 16 个 CPU 线程
--ctx-size 8192
:上下文长度(可生成的最大 Token 数)
--n-gpu-layers 7
:GPU 加速的层数(见上表)
--temp 0.6
:温度参数,DeepSeek 推荐值
--cache-type-k q4_0
:量化缓存类型
🍎 5. 在 Mac / Apple 设备上运行
🔹 注意 --n-gpu-layers
设定,如果 OOM(内存溢出),请减少此值!
📌 128GB 统一内存 Mac 可 offload 约 59 层:
./llama.cpp/llama-cli \
--model DeepSeek-R1-GGUF/DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
--cache-type-k q4_0 \
--threads 16 \
--prio 2 \
--temp 0.6 \
--ctx-size 8192 \
--seed 3407 \
--n-gpu-layers 59 \
-no-cnv \
--prompt "<|User|>Create a Flappy Bird game in Python.<|Assistant|>"
🦙 6. 在 Ollama / Open WebUI 上运行
🔹 Open WebUI 已提供官方指南 👉 教程
🔹 在 Ollama 运行前,需要 合并 3 个 GGUF 文件:
./llama.cpp/llama-gguf-split --merge \
DeepSeek-R1-GGUF/DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
merged_file.gguf
💡 7. 提示词(Prompt)示例
📌 完整任务描述:
Create a Flappy Bird game in Python. You must include these things:
- You must use pygame.
- The background color should be randomly chosen and is a light shade. Start with a light blue color.
- Pressing SPACE multiple times will accelerate the bird.
- The bird's shape should be randomly chosen as a square, circle, or triangle. The color should be randomly chosen as a dark color.
- Place on the bottom some land colored as dark brown or yellow chosen randomly.
- Make a score shown on the top right side. Increment if you pass pipes and don't hit them.
- Make randomly spaced pipes with enough space. Color them randomly as dark green or light brown or a dark gray shade.
- When you lose, show the best score. Make the text inside the screen. Pressing `q` or `Esc` will quit the game. Restarting is pressing SPACE again.
- The final game should be inside a markdown section in Python. Check your code for errors and fix them before the final markdown section.
📌 所有 18 个输出的 Python 代码已上传至 👉 docs.unsloth.ai
💕 8. 感谢支持!
感谢大家使用 & 分享 Unsloth! 🙏
📌 你可以:
- 加入我们的 Reddit 讨论组和 Discord 社区
- 在 Twitter 关注我们
- 订阅我们的 Newsletter
作者:Daniel & Michael Han 🦥
2025 年 1 月 27 日