4090单卡挑战DeepSeek r1 671b:尝试量化后的心得的分享

引言:

最近,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.10.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位量化 超级权重论文 我们成功应用了以下见解:

  1. 前三个密集层使用了所有权重的0.5%。我们将这些保持为4位或6位。

  2. MoE层使用共享专家,使用了1.5%的权重。我们将使用6位。

  3. 我们可以将所有MLA注意力模块保持为4位或6位,使用不到5%的权重。我们应该量化注意力输出(3%),但最好保持其较高精度。

  4. 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 的早期层中。

  1. 我们应该将 embedding 和 lm_head 分别保留为 4 位和 6 位。MoE 路由器和所有层归一化保留为 32 位。

  2. 这使得约 88% 的权重成为 MoE 权重!通过将它们量化为 1.58 位,我们可以大幅缩小模型!

  3. 我们提供了动态量化代码作为 llama.cpp 的一个分支:github.com/unslothai/llama.cpp

  4. 我们利用了 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后版本试试,但是可能也会失败的。只能说有这样挑战和冒险。

### 单个GPU上的DeepSeek配置与实施 对于单个GPU上运行DeepSeek,主要考虑的是优化资源利用效率以及确保模型训练和推理过程中的性能最大化。由于提到的内容涉及内存预取技术[^1],这同样适用于提升DeepSeek在单一GPU环境下的表现。 为了有效部署DeepSeek,在单个GPU环境中应采取如下措施: #### 配置环境变量 设置CUDA_VISIBLE_DEVICES来限定程序仅能访问指定编号的GPU设备。 ```bash export CUDA_VISIBLE_DEVICES=0 ``` #### 调整批处理大小 适当调整batch size可以充分利用GPU内存而不至于溢出。较小批量可能有助于更稳定的梯度下降路径,而较大批次则加速每轮更新的速度。需依据具体任务需求及可用显存容量做权衡。 #### 启用混合精度训练 采用FP16半精度浮点数代替默认的FP32全精度运算可减少一半的数据传输量并加快计算速度。PyTorch提供了一个简单接口`torch.cuda.amp.autocast()`用于开启自动混合精度支持。 ```python from torch.cuda import amp with amp.autocast(): output = model(input_tensor) loss.backward() optimizer.step() ``` #### 利用异步数据加载 通过多线程或多进程方式提前准备下一批次输入样本,使得I/O操作不会成为整个流程中的瓶颈。DataLoader类内置了num_workers参数控制子进程数量,默认设为零意味着同步执行。 ```python train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, num_workers=4) # 使用四个工作线程读取数据 ``` #### 应用内存预取策略 尽管上述方法侧重于软件层面改进,但在硬件条件允许情况下启用NVIDIA Tensor Core带来的TensorFloat-32(TF32)特性或Ampere架构特有的稀疏矩阵乘法(Sparse MatMul),均能在一定程度上模拟高效预取行为,从而改善整体吞吐率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值