[Deepseek-r1] 从源码部署问题分析

2 分钟阅读时长

发布时间:

目录

前言

想养女儿了, 结果只是想让女儿能听见就好困难, 作为一个微调苦手, 后来想了一下, 还是好好地重新开始从deepseek-r1学习, 才发现在进行小模型部署中存在了大量正常情况不会面临的问题, 于是打算在解决过程中把情况记录一下, 方便大家解决问题. 作为开源产品, 相比起望而却步不如先用起来.

下载

这也是一个比较抽象的问题, 数据源比较容易, 直接在huggingface找到最小模型, 考虑到大部分人难以下载, 也可以从镜像源进行拉取, 以下提供了hf-mirror. 但是源码在GitHub找到deepseek-r1会发现里面竟然只有论文, 喵了个咪, 谁教你这么用仓库的. 但是问题不大, 说明书有如下提示:

DeepSeek-R1 Models Please visit DeepSeek-V3 repo for more information about running DeepSeek-R1 locally.

NOTE: Hugging Face’s Transformers has not been directly supported yet.

注意了, 官方有说撇开责任说: 没有直接支持, 这是人干的事吗?! 可以先去deepseek-v3获取源码及相关说明书, 然后根据说明书调试下载的r1数据, 出问题的话就是后话了.

数据源问题

报错信息

跟随运行调整数据:

python convert.py --hf-ckpt-path /app/inference/DeepSeek-V3 --save-path /app/inference/DeepSeek-V3-Demo --n-experts 128 --model-parallel 4

可能会面临以下情况:

0%| | 0/163 [00:00<?, ?it/s]
Traceback (most recent call last):
File "convert.py", line 96, in
main(args.hf_ckpt_path, args.save_path, args.n_experts, args.model_parallel)
File "convert.py", line 51, in main
with safe_open(file_path, framework="pt", device="cpu") as f:
safetensors_rust.SafetensorError: Error while deserializing header: HeaderTooLarge

解决方法

我在#557上有帮忙过这个问题, 根源是git-lfs插件没有使用好, 导致.safetensors文件下载失败(或者假成功, 可以比较以下文件大小和hash), 可以通过下载lfs插件进行拉取:

apt install git git-lfs
git lfs install
git lfs clone https://...

对于archlinux可以使用:

sudo pacman -S git git-lfs
git lfs install
git clone https://...

或者直接在源上一个一个下载数据文件, 好处是到底有没有下载成功是明显能看见的, 解决以后应该可以正常处理了(吗?)

数据格式问题

报错信息

可以参考#330, 很遗憾我注意到的时候这个issues已经关闭了, 我们可以分析一下错误信息:

file: convert.py. line 63: assert key in mapping

分析

这是在使用convert.py脚本的过程中爆出的经典错误, 对main()函数(实际上函数内传入了4个变量, 不过因为只是对错误进行整理, 且官方代码中已经对变量进行了解释, 我们无需对变量进行研究)进行分析, 通过调试可以发现在model中R1的key模型节点相比R3预设中多了k_projv_proj节点, 我第一反应是找ds问个明白, 得到提示模型可能是其他的架构(如LLaMA).

这里有个小插曲, 朋友有使用oLLama快速部署了一个简单的模型, 但因为是被封装好的, 自行调整这一步相对会变得麻烦, 我还是想试试能不能尽可能透明地处理好属于自己的模型.

关键是”LLama”这个东西, 因为ds提出了在我认知中重合的东西, 且这个名字在其他deepseek-r1的相关数据模型中也存在过, 于是我尝试去了解了Llama, Llama是由Meta开源出来的一个大模型.

我们的思路应该换一换, 现在来认识一下DeepSeek-r1数据源的名字吧:

  • deepseek-ai
    • DeepSeek-R1: 在R1-Zero基础上解决可读性差和语言混合的模型
    • DeepSeek-R1-Zero: 以V3-Base为基础采用GRPO作为RL框架提高推理性能的模型
    • DeepSeek-R1-Distill-Llama-70B: 使用Llama模型的70B(十亿)数据蒸馏的模型
    • DeepSeek-R1-Distill-Qwen-32B: 使用Qwen模型的32B(十亿)数据蒸馏的模型
    • DeepSeek-R1-Distill-Qwen-14B: 使用Qwen模型的14B(十亿)数据蒸馏的模型
    • DeepSeek-R1-Distill-Llama-8B: 使用Llama模型的8B(十亿)数据蒸馏的模型
    • DeepSeek-R1-Distill-Qwen-7B: 使用Qwen模型的7B(十亿)数据蒸馏的模型
    • DeepSeek-R1-Distill-Qwen-1.5B: 使用Qwen模型的1.5B(十亿)数据蒸馏的模型

很遗憾Qwen的官方群并没有给出一个明确的回复(可能负责答疑的人也不理解那些节点到底是用来做什么的吧, 笑), 不过我找到了这样一段代码1:

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

class DeepSeekModel:
    def __init__(self, model_path="./DeepSeek-R1-Distill-Qwen-1.5B"):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        print(f"Using device: {self.device}")
        
        # 加载tokenizer和模型
        self.tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
        self.model = AutoModelForCausalLM.from_pretrained(
            model_path,
            trust_remote_code=True,
            torch_dtype=torch.float16 if self.device == "cuda" else torch.float32
        ).to(self.device)
        
        # 设置模型为评估模式
        self.model.eval()

    def generate_response(self, prompt, max_length=2048, temperature=0.7):
        try:
            # 对输入进行编码
            inputs = self.tokenizer(prompt, return_tensors="pt").to(self.device)
            
            # 生成回答
            with torch.no_grad():
                outputs = self.model.generate(
                    **inputs,
                    max_length=max_length,
                    temperature=temperature,
                    top_p=0.9,
                    pad_token_id=self.tokenizer.pad_token_id,
                    eos_token_id=self.tokenizer.eos_token_id
                )
            
            # 解码输出
            response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
            return response
        
        except Exception as e:
            print(f"生成回答时发生错误: {str(e)}")
            return None

def main():
    # 初始化模型
    model = DeepSeekModel()
    
    # 测试对话
    while True:
        user_input = input("\n请输入您的问题 (输入 'quit' 退出): ")
        if user_input.lower() == 'quit':
            break
            
        response = model.generate_response(user_input)
        if response:
            print("\nDeepSeek:", response)

if __name__ == "__main__":
    main()

当然在正式运行的时候最好根据自己的系统情况改善到适合运行, 但是我们应该明确重要的是, 至少现在我们可以用上这些我们不知道用途的数据了, 接下来 需要以这些代码为基础进行调试, (下次接着写).

解决方法

很遗憾我曾以为这个问题能很快解决, 但是需要进行更麻烦的处理, 在一切成功后我会尝试重新整理一份源码以供使用.

小结

很恨自己什么都不会, 每当看见身边的人努力后就能取得成绩, 相比之下自己是真的糟糕透了. 之前一场比赛因为自己的问题什么都没拿到, 结束后很痛苦, 有人问我以后有什么打算, 我说想好好学习一下AI了, 实际上一篇论文也没看懂.

后来刚好有deepseek的声音, 于是决定自己亲自部署一个(在此之前只是摸了一下minigpt, 源码看起来一言难尽), 过程中其实遇到了很多问题, 包括微调, 资源限制, 空闲时间太少等等等等, 我想起一位师傅说过: “要拼命地去学”, 实际上我也做不到拼命, 我太会逃避了, 不过很多人也是这样. 因为自己以往的一些经验, 遭遇错误后我尝试一点一点进行了排查, 才慢慢有了这篇文章, 往好了说我现在能够继续往下走了, 可是实际上从简历上看我也还是什么都没做到.

人生是一场痛苦的马拉松, 之前聊剧本的时候我想夹带一点私货, 我提出一个场景: 让女朋友抱着他说”没事了, 可以不用继续痛苦了”, 后来讨论了一下, 我们认为这个场景应该在死去以后以意识流的形式呈现. 虽然看起来有点地狱, 但是我有想过, 其实对于大部分人的痛苦是看不到尽头的.

越是温柔, 看到的越多, 也就越容易沉浸在绝望之中, 不想继续痛苦估计只有在一切结束了吧.

参考文献

  1. llama.本地化部署DeepSeek-R1-Distill-Qwen-1.5B[EB/OL].CSDN.https://blog.csdn.net/harebert/article/details/145392582.2025.01.29