From 109301e64363889266c6e79ea56e5782955bc97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=BF=97=E6=96=87?= <2671289934@qq.com> Date: Sat, 3 Jan 2026 20:12:35 +0800 Subject: [PATCH 1/6] add Ascend NPU support --- README.md | 18 + .../qwen3/01-Qwen3-8B-MindIE部署调用.md | 329 +++++++++++++ .../qwen3/02-Qwen3-8B-vLLM-ascend 部署调用.md | 432 ++++++++++++++++++ models_ascend/qwen3/images/01-01.png | Bin 0 -> 281369 bytes models_ascend/qwen3/images/01-02.png | Bin 0 -> 248711 bytes models_ascend/qwen3/images/02-01.png | Bin 0 -> 218675 bytes models_ascend/qwen3/images/02-02.png | Bin 0 -> 216502 bytes support_model_Ascend.md | 98 ++++ 8 files changed, 877 insertions(+) create mode 100644 models_ascend/qwen3/01-Qwen3-8B-MindIE部署调用.md create mode 100644 models_ascend/qwen3/02-Qwen3-8B-vLLM-ascend 部署调用.md create mode 100644 models_ascend/qwen3/images/01-01.png create mode 100644 models_ascend/qwen3/images/01-02.png create mode 100644 models_ascend/qwen3/images/02-01.png create mode 100644 models_ascend/qwen3/images/02-02.png create mode 100644 support_model_Ascend.md diff --git a/README.md b/README.md index 5d689ca..dfd07cb 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,24 @@ +### 昇腾Ascend NPU 专区 + +
+ 🚀 昇腾Ascend NPU 平台已支持模型
+ 每个模型都提供完整的昇腾Ascend NPU 环境配置和部署教程
+ 📖 查看完整昇腾 NPU 平台模型列表和教程
+
|
+ • Qwen3 + • Ascend NPU 环境配置通用指南 + • Ascend NPU 大模型推理性能优化建议 + |
+
diff --git a/models_ascend/qwen3/01-Qwen3-8B-MindIE部署调用.md b/models_ascend/qwen3/01-Qwen3-8B-MindIE部署调用.md
new file mode 100644
index 0000000..e3dd158
--- /dev/null
+++ b/models_ascend/qwen3/01-Qwen3-8B-MindIE部署调用.md
@@ -0,0 +1,329 @@
+# Qwen3-8B MindIE 部署调用
+## **MindIE 简介**
+
+昇腾 `MindIE` 推理引擎面向推理执行部署,使能应用高效落地。昇腾推理引擎,基于昇腾硬件的运行加速、调试调优、快速迁移部署的高性能深度学习推理框架,分层开放满足各类需求,统一接口使能极简开发,沉淀能力构筑极致性能。
+
+## 基础环境准备
+
+本文基础环境如下:
+
+```
+----------------
+ubuntu 22.04
+NPU驱动 25.2.0
+python 3.10
+cann 8.3.RC2
+torch 2.8.0
+torch-npu 2.8.0
+----------------
+```
+
+> 请确定昇腾NPU芯片的版本,目前支持A2、A3和310P系列产品。
+
+裸金属服务器或物理机可以通过[昇腾镜像仓库](https://www.hiascend.com/developer/ascendhub/detail/af85b724a7e5469ebd7ea13c3439d48f)获取设备对应镜像(使用AutoDL平台条跳过此步骤)
+
+
+
+> 考虑到部分同学配置环境可能会遇到一些问题,AutoDL 平台准备了MindIE推理引擎在昇腾设备运行的环境镜像,在租用实例时直接选择即可。
+⚠️注意:建议驱动版本选择25.2.0或以上版本,否则可能会遇到一些问题。
+
+
+
+首先 `pip` 换源加速下载并安装依赖包
+
+```shell
+# 升级pip
+python -m pip install --upgrade pip
+# 更换 pypi 源加速库的安装
+pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
+
+pip install modelscope
+```
+
+
+## 模型下载
+
+使用 modelscope 中的 snapshot_download 函数下载模型,第一个参数为模型名称,参数 cache_dir 为模型的下载路径。
+
+新建 `model_download.py` 文件并在其中输入以下内容,粘贴代码后记得保存文件。
+
+```python
+from modelscope import snapshot_download
+
+model_dir = snapshot_download('Qwen/Qwen3-8B', cache_dir='/root/autodl-tmp', revision='master')
+```
+
+然后在终端中输入 `python model_download.py` 执行下载,这里需要耐心等待一段时间直到模型下载完成。
+
+> 注意:记得修改 `cache_dir` 为你的模型下载路径哦~
+
+
+
+## 启动服务
+> 在菜单栏里输入 Lemonade Server 启动,点击菜单里中对应的图标
+
+
+
+然后选中对应的模型即可启动,可以对外输出兼容OpenAI的服务接口
+
+
+
+
+## 服务测试
+
+新建 `test.py` 文件并在其中输入以下内容,粘贴代码后请及时保存文件。以下代码有很详细的注释,大家如有不理解的地方,欢迎提出 issue 。
+```python
+#!/usr/bin/env python
+"""
+简洁版本的 API 测试脚本
+完全模仿 curl 请求
+"""
+import requests
+
+# 发送 POST 请求(完全对应 curl 命令)
+response = requests.post(
+ "http://localhost:8000/api/v1/chat/completions",
+ headers={"Content-Type": "application/json"},
+ json={
+ "model": "Qwen3-8B-Hybrid",
+ "messages": [{"role": "user", "content": "Hello!"}]
+ }
+)
+
+# 打印响应
+print(f"Status: {response.status_code}")
+print(f"Response: {response.json()}")
+
+
+```
+返回结果如下
+
+
+
+## 代码准备
+
+我们可也可以使用Lemonade API 构建API服务给其他应用调用服务,新建 `api.py` 文件并在其中输入以下内容,粘贴代码后请及时保存文件。以下代码有很详细的注释,大家如有不理解的地方,欢迎提出 issue 。
+```python
+from fastapi import FastAPI, Request
+from contextlib import asynccontextmanager
+from threading import Thread, Event
+from transformers import StoppingCriteriaList
+from lemonade.tools.server.serve import StopOnEvent
+from lemonade.api import from_pretrained
+from lemonade.tools.oga.utils import OrtGenaiStreamer
+import uvicorn
+import json
+import datetime
+import logging
+import queue
+import uuid
+import asyncio
+
+# 配置日志
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+# 全局模型变量
+model = None
+tokenizer = None
+
+# 任务队列和结果字典
+task_queue = queue.Queue()
+result_dict = {}
+worker_thread = None
+worker_running = False
+
+# --- 模型加载逻辑 ---
+def load_models():
+ global model, tokenizer
+ try:
+ logger.info("正在加载模型...")
+ model, tokenizer = from_pretrained(
+ "amd/Qwen3-8B-awq-quant-onnx-hybrid",
+ recipe="oga-hybrid",
+ )
+ logger.info("模型加载完成")
+ except Exception as e:
+ logger.error(f"模型加载失败: {str(e)}")
+ raise
+
+# --- 工作线程函数 ---
+def worker_thread_func():
+ """长期运行的工作线程,从队列中获取任务并处理"""
+ global model, tokenizer, task_queue, result_dict, worker_running
+
+ logger.info("工作线程已启动")
+ worker_running = True
+
+ while worker_running:
+ try:
+ # 从队列中获取任务(阻塞等待)
+ task = task_queue.get(timeout=1.0)
+
+ if task is None: # 收到停止信号
+ break
+
+ task_id, prompt_text, max_new_tokens = task
+
+ try:
+ # 使用 chat.py 中的推理方式
+ input_ids = tokenizer(prompt_text, return_tensors="pt").input_ids
+
+ # 使用流式生成器收集完整响应
+ streamer = OrtGenaiStreamer(tokenizer)
+ stop_event = Event()
+ stopping_criteria = StoppingCriteriaList([StopOnEvent(stop_event)])
+
+ generation_kwargs = {
+ "input_ids": input_ids,
+ "streamer": streamer,
+ "max_new_tokens": max_new_tokens,
+ "stopping_criteria": stopping_criteria,
+ }
+
+ # 在工作线程中创建子线程来运行 generate
+ # streamer 需要在另一个线程中读取,而 generate 在子线程中运行
+ generate_thread = Thread(target=model.generate, kwargs=generation_kwargs)
+ generate_thread.start()
+
+ # 在工作线程中收集完整响应(从 streamer 读取)
+ response = ""
+ for new_text in streamer:
+ response += new_text
+
+ # 等待生成线程完成
+ generate_thread.join()
+
+ # 将结果存入结果字典
+ result_dict[task_id] = {
+ "success": True,
+ "response": response
+ }
+
+ except Exception as e:
+ logger.error(f"处理任务 {task_id} 时出错: {str(e)}")
+ result_dict[task_id] = {
+ "success": False,
+ "error": str(e)
+ }
+
+ # 标记任务完成
+ task_queue.task_done()
+
+ except queue.Empty:
+ # 队列为空,继续循环
+ continue
+ except Exception as e:
+ logger.error(f"工作线程错误: {str(e)}")
+ continue
+
+ logger.info("工作线程已停止")
+
+# --- FastAPI应用 ---
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+ global worker_thread, worker_running
+
+ try:
+ load_models()
+
+ # 启动工作线程
+ worker_thread = Thread(target=worker_thread_func, daemon=True)
+ worker_thread.start()
+ logger.info("工作线程已启动")
+
+ yield
+
+ except Exception as e:
+ logger.error(f"服务初始化失败: {str(e)}")
+ raise
+ finally:
+ # 停止工作线程
+ worker_running = False
+ if worker_thread and worker_thread.is_alive():
+ task_queue.put(None) # 发送停止信号
+ worker_thread.join(timeout=5)
+ logger.info("工作线程已停止")
+
+app = FastAPI(lifespan=lifespan)
+
+# 处理POST请求的端点
+@app.post("/")
+async def create_item(request: Request):
+ global task_queue, result_dict # 声明全局变量
+
+ json_post_raw = await request.json() # 获取POST请求的JSON数据
+ json_post = json.dumps(json_post_raw) # 将JSON数据转换为字符串
+ json_post_list = json.loads(json_post) # 将字符串转换为Python对象
+ prompt = json_post_list.get('prompt') # 获取请求中的提示
+ history = json_post_list.get('history', []) # 获取请求中的历史记录
+ max_new_tokens = json_post_list.get('max_new_tokens', 1024) # 获取最大token数,默认1024
+
+ # 构建提示文本(处理历史记录)
+ if history:
+ conversation_text = ""
+ for item in history:
+ if isinstance(item, list) and len(item) == 2:
+ user_msg, assistant_msg = item
+ conversation_text += f"User: {user_msg}\nAssistant: {assistant_msg}\n"
+ prompt_text = f"{conversation_text}User: {prompt}\nAssistant:"
+ else:
+ prompt_text = prompt
+
+ try:
+ # 生成唯一任务ID
+ task_id = str(uuid.uuid4())
+
+ # 将任务放入队列
+ task_queue.put((task_id, prompt_text, max_new_tokens))
+ logger.info(f"任务 {task_id} 已加入队列")
+
+ # 等待结果(轮询检查结果字典)
+ max_wait_time = 300 # 最大等待时间(秒)
+ wait_interval = 0.1 # 轮询间隔(秒)
+ elapsed_time = 0
+
+ while task_id not in result_dict:
+ if elapsed_time >= max_wait_time:
+ raise TimeoutError(f"任务 {task_id} 超时")
+ await asyncio.sleep(wait_interval)
+ elapsed_time += wait_interval
+
+ # 获取结果
+ result = result_dict.pop(task_id)
+
+ if result["success"]:
+ response = result["response"]
+ now = datetime.datetime.now() # 获取当前时间
+ time = now.strftime("%Y-%m-%d %H:%M:%S") # 格式化时间为字符串
+ # 构建响应JSON
+ answer = {
+ "response": response,
+ "status": 200,
+ "time": time
+ }
+ # 构建日志信息
+ log = "[" + time + "] " + '", prompt:"' + prompt_text + '", response:"' + repr(response) + '"'
+ logger.info(log) # 打印日志
+ return answer # 返回响应
+ else:
+ raise Exception(result.get("error", "未知错误"))
+
+ except Exception as e:
+ logger.error(f"处理请求时出错: {str(e)}")
+ now = datetime.datetime.now()
+ time = now.strftime("%Y-%m-%d %H:%M:%S")
+ return {
+ "response": f"错误: {str(e)}",
+ "status": 500,
+ "time": time
+ }
+
+# 主函数入口
+if __name__ == '__main__':
+ # 模型加载在 lifespan 中自动完成
+ # 启动FastAPI应用
+ # 用6006端口可以将autodl的端口映射到本地,从而在本地使用api
+ uvicorn.run(app, host='0.0.0.0', port=6006, workers=1) # 在指定端口和主机上启动应用
+```
+
diff --git a/models_ascend/qwen3/02-Qwen3-8B-vLLM-ascend 部署调用.md b/models_ascend/qwen3/02-Qwen3-8B-vLLM-ascend 部署调用.md
new file mode 100644
index 0000000..27b08e9
--- /dev/null
+++ b/models_ascend/qwen3/02-Qwen3-8B-vLLM-ascend 部署调用.md
@@ -0,0 +1,432 @@
+# Qwen3-8B vllm-ascend 部署调用
+
+## **vllm-ascend 简介**
+
+`vllm-ascend` 是一个由社区维护的让vLLM在Ascend NPU无缝运行的后端插件。此插件是 vLLM 社区中支持昇腾后端的推荐方式。它遵循[RFC]: Hardware pluggable所述原则:通过解耦的方式提供了vLLM对Ascend NPU的支持。使用 vLLM 昇腾插件,可以让类Transformer、混合专家(MOE)、嵌入、多模态等流行的大语言模型在 Ascend NPU 上无缝运行。
+
+## 环境准备
+
+本文基础环境如下:
+
+```
+----------------
+ubuntu 22.04
+NPU驱动 25.2.0
+python 3.10
+cann 8.3.RC2
+torch 2.8.0
+torch-npu 2.8.0
+----------------
+```
+
+> 本文默认学习者已配置好以上 `Pytorch (CANN)` 环境,如未配置请先自行安装。
+
+> 请确定昇腾NPU芯片的版本,目前支持A2和A3系列产品。
+
+> 裸金属服务器或物理机可以通过docker pull获取设备对应镜像(使用AutoDL平台条跳过此步骤):
+```bash
+# A2系列产品
+docker pull quay.io/ascend/vllm-ascend:v0.13.0rc1
+# A3系列产品
+docker pull quay.io/ascend/vllm-ascend:v0.13.0rc1-a3
+```
+
+首先 `pip` 换源加速下载并安装依赖包:
+
+```bash
+python -m pip install --upgrade pip
+pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
+
+pip install modelscope
+
+# Install vllm-project/vllm. The newest supported version is v0.13.0.
+pip install vllm==0.13.0
+# Install vllm-project/vllm-ascend from pypi.
+pip install vllm-ascend==0.13.0rc1
+```
+
+> 考虑到部分同学配置环境可能会遇到一些问题,我们在 AutoDL 平台准备了 Qwen3 在昇腾设备运行的环境镜像,点击下方链接并直接创建 Autodl 示例即可。
+> ***https://www.codewithgpu.com/i/datawhalechina/self-llm/Qwen3***
+
+## 模型下载
+
+使用 modelscope 中的 snapshot_download 函数下载模型,第一个参数为模型名称,参数 cache_dir 为模型的下载路径。
+
+新建 `model_download.py` 文件并在其中输入以下内容,粘贴代码后记得保存文件。
+
+```python
+from modelscope import snapshot_download
+
+model_dir = snapshot_download('Qwen/Qwen3-8B', cache_dir='/root/autodl-tmp', revision='master')
+```
+
+然后在终端中输入 `python model_download.py` 执行下载,这里需要耐心等待一段时间直到模型下载完成。
+
+> 注意:记得修改 `cache_dir` 为你的模型下载路径哦~
+
+## **代码准备**
+
+### **Python 脚本**
+
+新建 `vllm_model.py` 文件并在其中输入以下内容,粘贴代码后请及时保存文件。下面的代码有很详细的注释,如有不理解的地方,欢迎大家提 `issue`。
+
+首先从 `vLLM` 库中导入 `LLM` 和 `SamplingParams` 类。`LLM` 类是使用 `vLLM` 引擎运行离线推理的主要类。`SamplingParams` 类指定采样过程的参数,用于控制和调整生成文本的随机性和多样性。
+
+`vLLM` 提供了非常方便的封装,我们直接传入模型名称或模型路径即可,不必手动初始化模型和分词器。
+
+然后,通过使用分词器的 apply_chat_template 函数,将我们的 prompt(提示词)格式化为模型所需的输入格式。
+
+默认情况下,Qwen3 启用了思考能力,类似于 QwQ-32B。这意味着该模型将利用其推理能力来提升生成回答的质量。例如,当在 tokenizer.apply_chat_template 中显式设置 enable_thinking=True 或保留其默认值时,模型将进入思考模式。
+
+我们可以通过这个代码示例熟悉下 ` vLLM` 引擎的使用方式。被注释的部分内容可以丰富模型的能力,但不是必要的,大家可以按需选择,自己多多动手尝试 ~
+
+```python
+import gc
+import torch
+from vllm import LLM, SamplingParams
+from transformers import AutoTokenizer
+import os
+import json
+
+# 自动下载模型时,指定使用modelscope; 否则,会从HuggingFace下载
+os.environ['VLLM_USE_MODELSCOPE']='True'
+
+def get_completion(prompts, model, tokenizer=None, temperature=0.6, top_p=0.95, top_k=20, min_p=0, max_tokens=4096, max_model_len=8192):
+ stop_token_ids = [151645, 151643]
+ # 创建采样参数。temperature 控制生成文本的多样性,top_p 控制核心采样的概率,top_k 通过限制候选词的数量来控制生成文本的质量和多样性, min_p 通过设置概率阈值来筛选候选词,从而在保证文本质量的同时增加多样性
+ sampling_params = SamplingParams(temperature=temperature, top_p=top_p, top_k=top_k, min_p=min_p, max_tokens=max_tokens, stop_token_ids=stop_token_ids) # max_tokens 用于限制模型在推理过程中生成的最大输出长度
+ # 初始化 vLLM 推理引擎
+ llm = LLM(model=model, tokenizer=tokenizer, max_model_len=max_model_len,trust_remote_code=True) # max_model_len 用于限制模型在推理过程中可以处理的最大输入和输出长度之和。
+ outputs = llm.generate(prompts, sampling_params)
+ return outputs
+
+
+if __name__ == "__main__":
+ # 初始化 vLLM 推理引擎
+ model='/root/autodl-tmp/Qwen/Qwen3-8B' # 指定模型路径
+ tokenizer = AutoTokenizer.from_pretrained(model, use_fast=False) # 加载分词器
+
+ prompt = "给我一个关于大模型的简短介绍。"
+ messages = [
+ {"role": "user", "content": prompt}
+ ]
+ text = tokenizer.apply_chat_template(
+ messages,
+ tokenize=False,
+ add_generation_prompt=True,
+ enable_thinking=True # 是否开启思考模式,默认为 True
+ )
+
+ outputs = get_completion(text, model, tokenizer=None, temperature=0.6, top_p = 0.95, top_k=20, min_p=0) # 对于思考模式,官方建议使用以下参数:temperature = 0.6,TopP = 0.95,TopK = 20,MinP = 0。
+
+ # 输出是一个包含 prompt、生成文本和其他信息的 RequestOutput 对象列表。
+ # 打印输出。
+ for output in outputs:
+ prompt = output.prompt
+ generated_text = output.outputs[0].text
+ print(f"Prompt: {prompt!r}, \nResponse: {generated_text!r}")
+```
+
+运行代码
+
+```bash
+python vllm_model.py
+```
+
+结果如下:
+
+```bash
+Prompt: '<|im_start|>user\n给我一个关于大模型的简短介绍。<|im_end|>\n<|im_start|>assistant\n',
+Response: ' HOrw^M4KU@do;nzV;
zY-(wdTuxEb1XAzQ;+!3#W;}nq#8;*QJmom;Uv}d5!^ D6aYqEVJa(xgzF%;us?`5A5wwT&MM r(~M6N;k8h)okXaYh_hkb0RPRZ4rvrIdF8)4rSeo!r-o_Z&}4KX0(L
zSBg;69a^J!M?s`Y9<@EM0U0jnovQka7Lo1Z8AfXuZ&}EKK`ULFqBYy_Ywx_CleB3F
zlS1}2t(&hv=f`-IojjNY@wu?a?FV5)sMGVhvDF#P`RO+YHQyTB-Cc<1XCBJZzC6xq
zN0+({c+SlRyH_%{&9g~7o6)mmk~6KRUN#aySuD%F_+~s!l1lKrQka
zfaT?@U=!iP26p`@)n3&-ftybRN#D7?`|e{o|E_Ri=^FhQxHqQ%u&pWP)=f|b-IB?V
ziN?MvKAI=?J>nygBs+T!^V&L-zZVHkA8TtIw9$*#sA$t>+kTbTHozEEX~XlLBq963
z0|_BPLwqdhgx_OK+zY=UTmJ5wS%W2x(AQx0a$2
#
o0LMu
z^k1a6{;F4JpB^oP5wY2#Ww9t|*yQge0
Z9((78B{a!noLOFZL}OT
zYFaBLzubiX;0CJfqx}Ji0wBb!>E|$Iu^z@F4_3?pFUP#um%GPQ=Nou6B95DulKPgyS6Lku6nQ2c=$vDO7yHg
zwN1d|GCRH5BF<8))ryx#38zY3I*XOtSVOA7(bb5;Uw;_&Q6lM)Ia^=an8%iJma
zQ5XHS69PEdOie~t8H