DeepSeek-R1 最近在完全开源的情况下与 OpenAI 的 O1 推理模型竞争,掀起了波澜。我们探索了如何让更多的本地用户运行它,并设法将 DeepSeek 的 R1 671B 参数模型量化为 131GB,比原来的 720GB 减少了 80%,同时功能强大。
通过研究 DeepSeek R1 的架构,我们设法选择性地将某些层量化为更高的位(如 4 位),并将大多数 MoE 层(如 GPT-4 中使用的层)保留为 1.5 位(参见Unsloth 动态 4 位).天真地量化所有层会完全破坏模型,导致无限循环和乱码输出。我们的动态量化解决了这个问题。
1.58 位量化应该适合 160GB 的 VRAM 以进行快速推理(2x H100 80GB),每秒达到大约 140 个令牌。您不需要 VRAM (GPU) 来运行 1.58 位 R1,只需 20GB 的 RAM (CPU) 就可以工作,但可能会很慢。为了获得最佳性能,我们建议 VRAM + RAM 的总和至少为 80GB+。
我们上传了大小从 131GB 到 212GB 不等的动态量化版本,以:huggingface.co/unsloth/DeepSeek-R1-GGUF
P.S. 如果您喜欢我们的工作,请随时给我们⭐加星标:github.com/unslothai/unsloth或在 X 上关注我们@UnslothAi💖
我们提供 4 个动态量化版本。前 3 个使用重要性矩阵来校准量化过程(通过 llama.cpp 的 imatrix)以允许较低位的表示。最后一个 212GB 版本是通用的 2 位量化,无需校准。
这些指令适用于R1蒸馏版和非蒸馏版模型,但请注意,它们对硬件的要求不同。有关R1的具体要求,请参见下文。
为了测试所有量化模型,我们没有依赖通用基准,而是让DeepSeek r1创建一个有3次尝试机会的Flappy Bird游戏(pass@3),并根据10项标准对其进行评分(例如使用随机颜色、随机形状、是否能在Python解释器中运行等)。我们使用了种子3407、3408和3409,并采用了建议的温度值0.6。
左侧是chat.deepseek.com生成的一个示例,右侧则是1.58位版本的结果。
我们惊讶地发现,我们的动态1.58位版本似乎仍然能够生成有效的输出! 然而,如果你不使用我们的动态1.58位版本,而是简单地量化所有层,你将会得到无限重复的内容,比如在种子3407中:“Colours with dark Colours with dark Colours with dark Colours with dark Colours with dark”,或者在种子3408中:“Set up the Pygame's Pygame display with a Pygame's Pygame's Pygame's Pygame's Pygame's Pygame's Pygame's Pygame's Pygame's”。 同样地,如果你不使用我们的动态版本,而是将所有层量化为1.75比特(149GB),无限重复会停止,但结果完全错误。所有输出都会产生全黑的屏幕。如果你将所有层量化为2.06比特(175GB),结果甚至比1.58比特(131GB)的动态量化还要差。你最好使用2.22比特(183GB)版本,它在性能上更优越。
1.58比特的动态量化有时会每8000个token产生1个错误的token,我们需要将其注释掉。使用min_p = 0.1
或0.05
应该可以缓解1.58比特版本生成单个错误token的问题。
总结一下,满分10分和Pass@3的得分如下:
我们在博客文章末尾提供了更详细的结果。
在我们之前对DeepSeek V3模型的分析中,该模型使用DeepSeek r1进行合成数据生成,我们注意到DeepSeek的前3层是完全密集的,而不是MoE(混合专家)。作为回顾,MoE(混合专家)层允许我们增加模型中的参数数量,而不会增加使用的FLOPs(浮点运算次数),因为我们动态地将大多数条目屏蔽为0,因此我们基本上跳过了对零化条目进行矩阵乘法运算。
MoEs(混合专家模型)的目标是“绕过”扩展定律,因为我们在不改变计算成本的情况下增加了参数数量。有关MoEs的更多笔记以及一种名为Memory Layers的新方法(旨在比MoEs做得更好),请参见这条推文:x.com/danielhanchen/status/1868748998783517093
通过结合以下四种方法,包括: 我们的4位动态量化方法 1.58位LLMs论文 Llama.cpp的1.5位量化 超级权重论文 我们成功应用了以下见解:
关于为什么所有的“超级权重”或最重要的权重都在 down_proj
中的主要见解是因为 SwiGLU 的操作:
[ [f(XW_{gate}) * (XW_{up})]W_{down} ] 这意味着 up
和 gate
投影本质上会相乘形成较大的数值,而 down_proj
必须将它们缩小——这意味着量化 down_proj
可能不是一个好主意,尤其是在 Transformer 的早期层中。
我们应该将 embedding
和 lm_head
分别保留为 4 位和 6 位。MoE 路由器和所有层归一化保留为 32 位。
这使得约 88% 的权重成为 MoE 权重!通过将它们量化为 1.58 位,我们可以大幅缩小模型!
我们提供了动态量化代码作为 llama.cpp 的一个分支:github.com/unslothai/llama.cpp
我们利用了 Bartowski 的重要性矩阵来进行低位量化。
所有蒸馏版本和主要的67IB R1模型使用相同的聊天模板:
< begin_of_sentence > < 用户 > 1+1等于多少?
< 助手 > 等于2。< end_of_sentence >
| 用户 | > 再解释一下!< 助手 |
在推理过程中,强制添加了BOS(开始符),并且每个交互之间用EOS(结束符)分隔。为了避免在推理过程中出现双BOS标记,你应该只调用tokenizer.encode(..., add_special_tokens = False)
,因为聊天模板会自动添加BOS标记。对于llama.cpp / GGUF推理,你应该跳过BOS,因为它会自动添加。
< 用户 > 1+1等于多少?< 助手 >
<think> | |||
</think> | |||
原始模型中的标记:
所有蒸馏版和原始 R1 版本似乎意外地将填充标记分配给了 < | end_of_sentence | >
,这通常不是一个好主意,特别是如果你想在这些推理模型的基础上进一步微调。这将导致无限生成,因为大多数框架会将 EOS 标记屏蔽为 -100。 我们修复了所有蒸馏版本和原始R1版本,使用了正确的填充标记(Qwen使用<|vision_pad|>
,Llama使用<|finetune_right_pad_id|>
,而R1使用<|PAD▁TOKEN|>
或我们自己添加的填充标记)。
你不需要使用新版本的 llama.cpp——任何能够运行 GGUF 文件的系统(如 Ollama、OpenWebUI、Transformers,甚至 vLLM)都应该能够运行动态量化(dynamic quants)。如果你的 VRAM 或 RAM 不足,可能会比较慢,但它是可以运行的。
如果你想直接使用 llama.cpp,请按照这里的 llama.cpp 构建说明操作——别忘了启用 GPU 支持!我通常使用以下命令:
apt-get update
apt-get install build-essential cmake curl libcurl4-openssl-dev -y
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
cp llama.cpp/build/bin/llama-* llama.cpp
然后通过huggingface.co/unsloth/DeepSeek-R1-GGUF下载模型。您可以使用Hugging Face进行此操作。要下载1.58bit版本,请执行以下操作:
# pip install huggingface_hub
from huggingface_hub import snapshot_download
snapshot_download(
repo_id = "unsloth/DeepSeek-R1-GGUF",
local_dir = "DeepSeek-R1-GGUF",
allow_patterns = [“*UD-IQ1_S*”],
)
[n_{\text{offload}} = \frac{\text{VRAM}(GB)}{\text{文件大小}(GB)} \times n_{\text{层数}} - 4]
要运行模型,我们将K缓存量化为4bit。量化V缓存需要为llama.cpp编译flash attention内核。我们使用机器上的所有线程,并使用DeepSeek推荐的温度0.6。上下文大小是您希望模型生成的令牌数量。
./llama.cpp/llama-cli \
--model DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
--cache-type-k q4_0 \
--threads 12 -no-cnv --n-gpu-layers 61 --prio 2 \
--temp 0.6 \
--ctx-size 8192 \
--seed 3407 \
--prompt "<|User|>Create a Flappy Bird game in Python.<|Assistant|>”
如果你想使用Ollama或vLLM对GGUF文件进行推理,你需要先将3个GGUF分片文件合并成1个,如下面的代码所示。然后你需要在本地运行该模型。
./llama.cpp/llama-gguf-split --merge \
DeepSeek-R1-UD-IQ1_S/DeepSeek-R1-UD-IQ1_S-00001-of-00003.gguf \
merged_file.gguf
使用的完整提示如下:用Python创建Flappy Bird游戏。您必须包括以下内容:
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.
最终的游戏应该在Python的markdown部分。检查代码中的错误,并在最后的markdown部分之前修复它们。 完整的表格和结果位于:docs.unsloth.ai/basics/deptheek-r1-dynamic-1.58-bit所有18个输出和Python生成的代码也上传到那里!