引言:
最近,DeepSeek-R1在完全开源的背景下,与OpenAI的O1推理模型展开了激烈竞争,引发了广泛关注。为了让更多本地用户能够运行DeepSeek,我们成功将R1 671B参数模型从720GB压缩至131GB,减少了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+。
1:使用动态量化版本
使用重要性矩阵来校准量化过程(通过 llama.cpp 的 imatrix)以允许较低位的表示。
MoE Bits | Disk Size | Type | Quality | Link | Down_proj |
---|---|---|---|---|---|
1.58-bit | 131GB | IQ1_S | Fair | Link | 2.06/1.56bit |
这些指令适用于R1蒸馏版和非蒸馏版模型,但请注意,它们对硬件的要求不同。
2. 基准和消融
为了测试所有量化模型,我们没有依赖通用基准,而是让DeepSeek r1创建一个有3次尝试机会的Flappy Bird游戏(pass@3),并根据10项标准对其进行评分(例如使用随机颜色、随机形状、是否能在Python解释器中运行等)。我们使用了种子3407、3408和3409,并采用了建议的温度值0.6~0.7之间
3:原创和量化后的对比数据
我们惊讶地发现,我们的动态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),无限重复会停止,但结果完全错误。
Model Size | Dynamic Quant | Model Size | Basic Quant |
---|---|---|---|
131GB | 6.92 | 133GB | 0 |
1.58比特的动态量化有时会每8000个token产生1个错误的token,我们需要将其注释掉。使用min_p = 0.1
或0.05
应该可以缓解1.58比特版本生成单个错误token的问题。
4.利用DeepSeek R1的架构
在我们之前对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位量化 超级权重论文 我们成功应用了以下见解:
-
前三个密集层使用了所有权重的0.5%。我们将这些保持为4位或6位。
-
MoE层使用共享专家,使用了1.5%的权重。我们将使用6位。
-
我们可以将所有MLA注意力模块保持为4位或6位,使用不到5%的权重。我们应该量化注意力输出(3%),但最好保持其较高精度。
-
down_proj对量化最为敏感,尤其是在前几层。我们通过超级权重论文、我们的动态量化方法和llama.cpp的GGUF量化方法验证了我们的发现。因此,我们将前3到6个MoE down_proj矩阵保持较高精度。例如,在超级权重论文中,我们看到几乎所有不应被量化的权重都在down_proj中。

关于为什么所有的"超级权重"或最重要的权重都在 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等于多少?< 助手 >
和标记有自己指定的标记。对于Qwen和Llama的蒸馏版本,一些标记被重新映射,例如Qwen没有BOS标记,所以必须使用<\object_ref_start>代替。所有蒸馏版和原始 R1 版本似乎意外地将填充标记分配给了 < | end_of_sentence | >
,这通常不是一个好主意,特别是如果你想在这些推理模型的基础上进一步微调。这将导致无限生成,因为大多数框架会将 EOS 标记屏蔽为 -100。 我们修复了所有蒸馏版本和原始R1版本,使用了正确的填充标记(Qwen使用<|vision_pad|>
,Llama使用<|finetune_right_pad_id|>
,而R1使用<|PAD▁TOKEN|>
或我们自己添加的填充标记)。
DeepSeek R1有61层。例如,使用24GB GPU或80GB GPU时,您可以预期在向下取整后卸载(如果内存不足,减少11)
要运行模型,我们将K缓存量化为4bit。量化V缓存需要为llama.cpp编译flash attention内核。我们使用机器上的所有线程,并使用DeepSeek推荐的温度0.6。上下文大小是您希望模型生成的令牌数量。
到此我可以尝试一下用运行量化后的deepseek R1 671B后版本试试,但是可能也会失败的。只能说有这样挑战和冒险。