Toybrick

本地和开发板部署 DeepSeek-R1 和 Janus-Pro

jefferyzhang

版主

积分
14138
楼主
发表于 2025-2-1 15:52:44    查看: 3788|回复: 16 | [复制链接]    打印 | 只看该作者
本帖最后由 jefferyzhang 于 2025-2-6 15:05 编辑

目录


* DeepSeek-R1系列模型硬件要求 -- 本楼
* Janus-Pro系列模型硬件要求 -- 本楼
* 常用模型下载地址 -- 本楼
* DeepSeek-R1(文生文模型) PC本地部署 -- 沙发
* Janus-Pro (文生图模型)PC本地部署 -- 板凳
* DeepSeek-R1 RK35XX部署 -- 地板
* 总结与技术应用综述 -- 5楼



DeepSeek-R1系列模型硬件要求

模型名称 模型参数大小 RAM需求(FP16) RAM需求(INT8) RAM需求(INT4)
DeepSeek-R1 671B>=2600GB>=1400GB>=700GB
DeepSeek-R1-Distill-Qwen-1.5B 1.5B>=8GB>=4GB>=2GB
DeepSeek-R1-Distill-Qwen-7B 7B>= 16GB>=8GB>=4GB
DeepSeek-R1-Distill-Llama-8B 8B>= 16GB>=8GB>=4GB
DeepSeek-R1-Distill-Qwen-14B 14B>= 32GB>=16GB>=8GB
DeepSeek-R1-Distill-Qwen-32B32B>= 64GB>=32GB>=16GB
DeepSeek-R1-Distill-Llama-70B70B>= 140GB>=70GB>=35GB


Janus-Pro系列模型硬件要求

模型名称 模型参数大小 RAM需求(FP16) RAM需求(INT8) RAM需求(INT4)
Janus-Pro-7B 7B>=28GB>=14GB>=7GB
Janus-Pro-1B 1B>=4GB>=2GB>=1GB



备注说明:

1. 数值为理论值,根据系统负载可能会不一致
2. 如果是在PC上使用GPU加速,RAM需求就是为显存需求(内存也不能小于显存大小)
3. 如果是在PC上使用CPU运行,RAM需求就是为内存需求
4. 如果在RK35XX芯片上部署,RAM需求就是DDR大小需求(因为NPU和CPU访问的是同一个DDR空间)
5. 量化因为精度调试要求,并不会所有参数都量化到指定类型,所以实际DDR需求在原始模型和量化模型之间,例如使用rknn工具做w4a8量化(即权重4bit量化,激活值8bit量化),那么DeepSeek-R1-Distill-Qwen-7B实际的量化模型需求会在4G~8GB之间,具体请参看rknn-toolkit和rknn-llm文档
6. 基于性价比考虑,我们在RKNPU端侧上应用部署主要关注7B/8B和1B/1.5B模型




模型下载地址

https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Llama-8B

https://huggingface.co/deepseek-ai/Janus-Pro-1B
https://huggingface.co/deepseek-ai/Janus-Pro-7B



回复

使用道具 举报

jefferyzhang

版主

积分
14138
沙发
 楼主| 发表于 2025-2-1 16:35:53 | 只看该作者
本帖最后由 jefferyzhang 于 2025-2-2 10:43 编辑

DeepSeek-R1 (文生文模型) PC本地部署

1. 系统环境

* 我们以windows wsl2为例,ubuntu 2404 单系统相同,安装GPU加速支持请参看对应设备的文档。
* 接口框架选用ollama
* WSL2建议网络模式使用mirror模式,这样wsl和host共用ip和所有端口,方便访问。
* WSL2不建议打开systemd,用的时候手动开ollama即可,避免开机自动启动

2. 安装ollama

进入wsl ubuntu命令行后,执行:
  1. curl -fsSL https://ollama.com/install.sh | sh
复制代码


3. 运行ollama服务(无systemd情况下手动运行)

  1. ollama serve
复制代码
然后在host端浏览器打开提示地址(例如127.0.0.1:11434),看到“Ollama is running”即成功运行

4. 安装deepseek-r1 (以8b为例)

  1. ollama run deepseek-r1:8b
复制代码


5. 成功运行

模型自动下载成功后,出现 >>> Send a message  代表已经运行成功了,直接输入即可对话,
UI或者其他APP接入时候需要打开wsl中的ollama服务,配置对应的api地址即可。


参考文档:

* OLLAMA官方安装地址:Ollama
* WSL2官方安装文档:安装 WSL | Microsoft Learn
* WSL2 镜像网络模式设置:镜像模式网络
* WSL2 NVIDIA 驱动: 在 WSL 2 上启用 NVIDIA CUDA | Microsoft Learn

回复

使用道具 举报

jefferyzhang

版主

积分
14138
板凳
 楼主| 发表于 2025-2-1 16:36:04 | 只看该作者
本帖最后由 jefferyzhang 于 2025-2-6 15:55 编辑

Janus-Pro (文生图模型)PC本地部署

1. 本地环境


* 我们以windows wsl2为例,ubuntu 2404 单系统相同,安装GPU加速支持请参看对应设备的文档。
* Python环境建议使用miniforge替代conda,当然你也可以直接使用系统环境。
* WebUI安装Janus-Pro的文档建议,使用gradio
* WSL2建议网络模式使用mirror模式,这样wsl和host共用ip和所有端口,方便访问。
* WSL2不建议打开systemd,用的时候手动开ollama即可,避免开机自动启动


2. 搭建python环境(如果使用系统python环境可跳过)


  1. conda create -n janus python=3.11 -y
  2. conda activate janus
复制代码


3. 搭建janus pro环境

(建议将requirements.txt中pytorch版本改为2.2.2,避免CUDA遇到BFloat16错误)
  1. git clone https://github.com/deepseek-ai/Janus.git
  2. pip install -r Janus/requirements.txt
复制代码


4. 安装完成,启动ui

(demo/app_januspro.py中默认使用7B的模型,可以手动改为其他大小的模型)
  1. cd Janus
  2. export PYTHONPATH=`pwd`

  3. # 直接使用CPU运行
  4. python demo/app_januspro.py

  5. # 或者使用NVIDIA GPU加速运行
  6. python demo/app_januspro.py --device cuda
复制代码

5. 运行成功后出现IP地址,直接浏览器打开即可使用
(例如:127.0.0.1:7860)



参考资料:


* Miniforge安装: GitHub - conda-forge/miniforge: A conda-forge distribution.
* WSL2官方安装文档:安装 WSL | Microsoft Learn
* WSL2 镜像网络模式设置:镜像模式网络
* WSL2 NVIDIA 驱动: 在 WSL 2 上启用 NVIDIA CUDA | Microsoft Learn

回复

使用道具 举报

jefferyzhang

版主

积分
14138
地板
 楼主| 发表于 2025-2-1 16:36:24 | 只看该作者
本帖最后由 jefferyzhang 于 2025-2-11 14:37 编辑

DeepSeek-R1 RK35XX部署


( 官方demo已经更新: rknn-llm/examples/DeepSeek-R1-Distill-Qwen-1.5B_Demo at main · airockchip/rknn-llm )


1. 系统RKNN环境搭建

* 下载rknn-llm : airockchip/rknn-llm
* 按其文档搭建rknn-llm/rknn-toolkit2环境

2. 下载DeepSeek-R1-1.5B HunggingFace 模型

* 新建一个目录,把这里所有文件下下来
deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B at main

3. 编写转换脚本,放到deepseek模型目录里

  1. from rkllm.api import RKLLM
  2. from datasets import load_dataset
  3. from transformers import AutoTokenizer
  4. from tqdm import tqdm
  5. import torch
  6. from torch import nn
  7. import os
  8. # os.environ['CUDA_VISIBLE_DEVICES']='1'

  9. modelpath = '.'
  10. llm = RKLLM()

  11. # Load model
  12. # Use 'export CUDA_VISIBLE_DEVICES=2' to specify GPU device
  13. # options ['cpu', 'cuda']
  14. ret = llm.load_huggingface(model=modelpath, model_lora = None, device='cpu')
  15. # ret = llm.load_gguf(model = modelpath)
  16. if ret != 0:
  17.     print('Load model failed!')
  18.     exit(ret)

  19. # Build model
  20. dataset = "./data_quant.json"
  21. # Json file format, please note to add prompt in the input,like this:
  22. # [{"input":"Human: 你好!\nAssistant: ", "target": "你好!我是人工智能助手KK!"},...]

  23. qparams = None
  24. # qparams = 'gdq.qparams' # Use extra_qparams
  25. #ret = llm.build(do_quantization=True, optimization_level=1, quantized_dtype='w8a8',
  26. #                quantized_algorithm='normal', target_platform='rk3588', num_npu_core=3, extra_qparams=qparams, dataset=dataset)

  27. ret = llm.build(do_quantization=True, optimization_level=1, quantized_dtype='w8a8',
  28.                 quantized_algorithm='normal', target_platform='rk3576', num_npu_core=2, extra_qparams=qparams, dataset=dataset)

  29. if ret != 0:
  30.     print('Build model failed!')
  31.     exit(ret)

  32. # Evaluate Accuracy
  33. def eval_wikitext(llm):
  34.     seqlen = 512
  35.     tokenizer = AutoTokenizer.from_pretrained(
  36.         modelpath, trust_remote_code=True)
  37.     # Dataset download link:
  38.     # https://huggingface.co/datasets/Salesforce/wikitext/tree/main/wikitext-2-raw-v1
  39.     testenc = load_dataset(
  40.         "parquet", data_files='./wikitext/wikitext-2-raw-1/test-00000-of-00001.parquet', split='train')
  41.     testenc = tokenizer("\n\n".join(
  42.         testenc['text']), return_tensors="pt").input_ids
  43.     nsamples = testenc.numel() // seqlen
  44.     nlls = []
  45.     for i in tqdm(range(nsamples), desc="eval_wikitext: "):
  46.         batch = testenc[:, (i * seqlen): ((i + 1) * seqlen)]
  47.         inputs = {"input_ids": batch}
  48.         lm_logits = llm.get_logits(inputs)
  49.         if lm_logits is None:
  50.             print("get logits failed!")
  51.             return
  52.         shift_logits = lm_logits[:, :-1, :]
  53.         shift_labels = batch[:, 1:].to(lm_logits.device)
  54.         loss_fct = nn.CrossEntropyLoss().to(lm_logits.device)
  55.         loss = loss_fct(
  56.             shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1))
  57.         neg_log_likelihood = loss.float() * seqlen
  58.         nlls.append(neg_log_likelihood)
  59.     ppl = torch.exp(torch.stack(nlls).sum() / (nsamples * seqlen))
  60.     print(f'wikitext-2-raw-1-test ppl: {round(ppl.item(), 2)}')

  61. # eval_wikitext(llm)


  62. # Chat with model
  63. messages = "<|im_start|>system You are a helpful assistant.<|im_end|><|im_start|>user你好!\n<|im_end|><|im_start|>assistant"
  64. kwargs = {"max_length": 128, "top_k": 1, "top_p": 0.8,
  65.           "temperature": 0.8, "do_sample": True, "repetition_penalty": 1.1}
  66. # print(llm.chat_model(messages, kwargs))


  67. # Export rkllm model
  68. ret = llm.export_rkllm("./deepseek-r1.rkllm")
  69. if ret != 0:
  70.     print('Export model failed!')
  71.     exit(ret)
复制代码


4. 如果需要做精度评估,就按代码里下载数据集做精度评估,如果需要调整量化策略,就在上头相应修改。执行该脚本,就能得到转换后的模型 deepseek-r1.rknn

5. 开发板部署运行(编写基于rkllm_api的开发板程序)

  1. #include <cstdio>
  2. #include <cstdint>
  3. #include <cstdlib>
  4. #include <cstring>
  5. #include <string>
  6. #include <iostream>
  7. #include <fstream>
  8. #include <vector>
  9. #include <csignal>

  10. #include "rkllm.h"

  11. #define MODEL_PATH "/data/deepseek-r1_3588_w8a8.rkllm"
  12. #define PROMPT_TEXT_PREFIX "<|User|>"
  13. #define PROMPT_TEXT_POSTFIX "<|Assistant|>"

  14. LLMHandle llmHandle = nullptr;

  15. void exit_handler(int signal) {
  16.     if (llmHandle != nullptr)
  17.     {
  18.         {
  19.             std::cout << "程序即将退出" << std::endl;
  20.             LLMHandle _tmp = llmHandle;
  21.             llmHandle = nullptr;
  22.             rkllm_destroy(_tmp);
  23.         }
  24.     }
  25.     exit(signal);
  26. }

  27. void callback(RKLLMResult *result, void *userdata, LLMCallState state) {
  28.     if (state == RKLLM_RUN_FINISH) {
  29.         printf("\n");
  30.     } else if (state == RKLLM_RUN_ERROR) {
  31.         printf("\\run error\n");
  32.     } else if (state == RKLLM_RUN_GET_LAST_HIDDEN_LAYER) {
  33.         /* ================================================================================================================
  34.         若使用GET_LAST_HIDDEN_LAYER功能,callback接口会回传内存指针:last_hidden_layer,token数量:num_tokens与隐藏层大小:embd_size
  35.         通过这三个参数可以取得last_hidden_layer中的数据
  36.         注:需要在当前callback中获取,若未及时获取,下一次callback会将该指针释放
  37.         ===============================================================================================================*/
  38.         if (result->last_hidden_layer.embd_size != 0 && result->last_hidden_layer.num_tokens != 0) {
  39.             int data_size = result->last_hidden_layer.embd_size * result->last_hidden_layer.num_tokens * sizeof(float);
  40.             printf("\ndata_size:%d",data_size);
  41.             std::ofstream outFile("last_hidden_layer.bin", std::ios::binary);
  42.             if (outFile.is_open()) {
  43.                 outFile.write(reinterpret_cast<const char*>(result->last_hidden_layer.hidden_states), data_size);
  44.                 outFile.close();
  45.                 std::cout << "Data saved to output.bin successfully!" << std::endl;
  46.             } else {
  47.                 std::cerr << "Failed to open the file for writing!" << std::endl;
  48.             }
  49.         }
  50.     } else if (state == RKLLM_RUN_NORMAL) {
  51.         printf("%s", result->text);
  52.     }
  53. }

  54. int main() {
  55.     signal(SIGINT, exit_handler);
  56.     printf("rkllm init start\n");

  57.     //设置参数及初始化
  58.     RKLLMParam param = rkllm_createDefaultParam();
  59.     param.model_path = MODEL_PATH;

  60.     //设置采样参数
  61.     param.top_k = 1;
  62.     param.top_p = 0.95;
  63.     param.temperature = 0.8;
  64.     param.repeat_penalty = 1.1;
  65.     param.frequency_penalty = 0.0;
  66.     param.presence_penalty = 0.0;

  67.     param.max_new_tokens = 128000;
  68.     param.max_context_len = 128000;
  69.     param.skip_special_token = true;
  70.     param.extend_param.base_domain_id = 0;

  71.     int ret = rkllm_init(&llmHandle, ¶m, callback);
  72.     if (ret == 0){
  73.         printf("rkllm init success\n");
  74.     } else {
  75.         printf("rkllm init failed\n");
  76.         exit_handler(-1);
  77.     }

  78.     std::string text;
  79.     RKLLMInput rkllm_input;

  80.     // 初始化 infer 参数结构体
  81.     RKLLMInferParam rkllm_infer_params;
  82.     memset(&rkllm_infer_params, 0, sizeof(RKLLMInferParam));  // 将所有内容初始化为 0

  83.     rkllm_infer_params.mode = RKLLM_INFER_GENERATE;

  84.     while (true)
  85.     {
  86.         std::string input_str;
  87.         printf("\n");
  88.         printf("user: ");
  89.         std::getline(std::cin, input_str);
  90.         if (input_str == "exit")
  91.         {
  92.             break;
  93.         }

  94.         text = PROMPT_TEXT_PREFIX + input_str + PROMPT_TEXT_POSTFIX;
  95.         rkllm_input.input_type = RKLLM_INPUT_PROMPT;
  96.         rkllm_input.prompt_input = (char *)text.c_str();
  97.         printf("robot: ");

  98.         // 若要使用普通推理功能,则配置rkllm_infer_mode为RKLLM_INFER_GENERATE或不配置参数
  99.         rkllm_run(llmHandle, &rkllm_input, &rkllm_infer_params, NULL);
  100.     }
  101.     rkllm_destroy(llmHandle);

  102.     return 0;
  103. }
复制代码


需要注意的是:

1. 如果你是copy自demo,请去掉场景提示词,deepseek不需要提示词。
2. 终端注意输入的汉字编码
3. RK3588 8bit量化内存需要8G或者8G以上,运行时DDR占用70%左右。
4. RK3576 4bit量化内存需要4G或者4G以上,运行时DDR占用80%左右。


回复

使用道具 举报

jefferyzhang

版主

积分
14138
5#
 楼主| 发表于 2025-2-4 17:20:01 | 只看该作者
本帖最后由 jefferyzhang 于 2025-2-5 16:11 编辑

总结与技术应用综述

1. 此次deepseek-r1模型性价比成本非常的高,去年这时候部署过rknn的llama-1.5b,可以说是惨不忍睹。但此次的deepseek-r1-1.5b的效果极佳,超过了llama-7b甚至可以达到70b的效果。
2. deepseek-r1中文使用体验非常的棒,超过了以往所有模型。
3. Janus-Pro 蒸馏模型效果不好,7b在PC端显卡运行都极其的慢,1B效果极差,就不做端侧部署了,意义不大。
4. deepseek-r1思考过程比较耗费时间,导致7b,8b模型在端侧运行出最终思考结果速度也极慢,应用意义上就小了,建议还是用1.5b做应用。
5. deepseek-r1-1.5b做成安卓和linux接口后可以围绕该模型开发非常多的应用形态,例如人机交互、应用提示、游戏AI、机器人等,可以说打开了一个新世界大门。
回复

使用道具 举报

erquren

中级会员

积分
258
6#
发表于 2025-2-5 15:56:44 | 只看该作者
牛逼啊老哥,春节加班了,给你点个赞
回复

使用道具 举报

jefferyzhang

版主

积分
14138
7#
 楼主| 发表于 2025-2-5 16:17:55 | 只看该作者
erquren 发表于 2025-2-5 15:56
牛逼啊老哥,春节加班了,给你点个赞

自己玩时候顺手写了一个,转换过程很顺利,没碰到什么问题。
这模型确实牛逼,可玩性非常大了可以说是
回复

使用道具 举报

erquren

中级会员

积分
258
8#
发表于 2025-2-6 10:40:15 | 只看该作者
我用转换后的1.5B模型跑,经常问中文吐英文,你有遇到这个情况吗?
链接: https://pan.baidu.com/s/1bX6uIfRT4FstbYkWfEjezw?pwd=vngk 提取码: vngk
回复

使用道具 举报

jefferyzhang

版主

积分
14138
9#
 楼主| 发表于 2025-2-6 14:16:53 | 只看该作者
erquren 发表于 2025-2-6 10:40
我用转换后的1.5B模型跑,经常问中文吐英文,你有遇到这个情况吗?
链接: https://pan.baidu.com/s/1bX6uIf ...

Deepseek不需要提示词,你抄demo时候记得把demo提示词注释掉,直接对话即可
回复

使用道具 举报

erquren

中级会员

积分
258
10#
发表于 2025-2-6 14:30:36 | 只看该作者
jefferyzhang 发表于 2025-2-6 14:16
Deepseek不需要提示词,你抄demo时候记得把demo提示词注释掉,直接对话即可

text = PROMPT_TEXT_PREFIX + input_str + PROMPT_TEXT_POSTFIX;  就这个改成
text = input_str; 其他没改的把
然后说几句话就开始复读机模式,还切英语,
https://gist.github.com/erquren/de63fa5cfbb09aa889dc5d97135c002a
你没出过这个情况吗?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

产品中心 购买渠道 开源社区 Wiki教程 资料下载 关于Toybrick


快速回复 返回顶部 返回列表