外观
N100中使用docker部署Qwen3.5-0.8b-MNN
准备在绿联上跑个Qwen的模型,N100的U,8G内存。查了半天发现了MNN
编译MNN
构建目录
root@85:/home/qwen# tree -a -L 1
|-- .dockerignore
|-- .env
|-- Dockerfile
|-- docker-compose.yml
|-- model
`-- src根据官方文档的部署:
下载MNN框架,编译
MNN (Mobile Neural Network),阿里开源的轻量级深度学习推理框架
部署Qwen需要通过MNN-LLM,MNN-LLM是基于MNN开发的
#https://mnn-docs.readthedocs.io/en/latest/transformers/llm.html?highlight=llm_demo
cd /home/qwen
rm -rf MNN-3.4.0
#3.4无法跑Qwen3.5
#git clone --recurse-submodules https://github.com/alibaba/MNN.git -b 3.4.0 MNN-3.4.0
git clone --recurse-submodules https://github.com/alibaba/MNN.git -b mmaster MNN
cd MNN
rm -rf build && mkdir build && cd build
# 官方推荐参数 + N100 优化
cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DMNN_BUILD_LLM=true \
-DMNN_SUPPORT_TRANSFORMER_FUSE=true \
-DMNN_CPU_WEIGHT_DEQUANT_GEMM=true \
-DMNN_LOW_MEMORY=true \
-DMNN_AVX2=true \
-DMNN_AVX512=false \
-DMNN_SEP_BUILD=ON \ # ← 关键:改为 ON,使用动态库方式
-DMNN_USE_SYSTEM_LIB=OFF \
-DMNN_BUILD_TOOLS=true \
-DMNN_USE_THREAD_POOL=true
cmake .. -DCMAKE_BUILD_TYPE=Release -DMNN_BUILD_LLM=true -DMNN_SUPPORT_TRANSFORMER_FUSE=true -DMNN_CPU_WEIGHT_DEQUANT_GEMM=true -DMNN_LOW_MEMORY=true -DMNN_AVX2=true -DMNN_AVX512=false -DMNN_SEP_BUILD=ON -DMNN_USE_SYSTEM_LIB=OFF -DMNN_BUILD_TOOLS=true -DMNN_USE_THREAD_POOL=true
# 编译
make -j4
# 验证产物
ls -lh llm_demo libMNN.so libllm.so
echo "你好,简单介绍下自己" > prompt.txt
./llm_demo ../../Qwen2.5-0.5B-Instruct-MNN/config.json prompt.txt
../mnn_runtime/bin/llm_demo config.json ../prompt.txt
../mnn_runtime/bin/llm_demo config.json检查依赖
ldd /home/qwen/mnn_runtime/bin/llm_demo
root@2d4e1355223f:/app/bin# ldd llm_demo
linux-vdso.so.1 (0x00007ffec5f84000)
libllm.so => /app/lib/libllm.so (0x00007f64c3b05000)
libMNN_Express.so => /app/lib/libMNN_Express.so (0x00007f64c3a11000)
libMNN.so => /app/lib/libMNN.so (0x00007f64c3727000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f64c3508000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f64c3326000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f64c3244000)
/lib64/ld-linux-x86-64.so.2 (0x00007f64c3c3b000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f64c3224000)下载模型
https://modelscope.cn/models/MNN/Qwen2.5-0.5B-Instruct-MNN 可以运行 https://modelscope.cn/models/MNN/Qwen3.5-0.8B-MNN 一直遇到3.5运行时模型初始化错误。
坑了一天,最后发现 Qwen3.5-0.8B-MNN报错的原因是因为下载了MNN3.4.0版本,他不支持3.5的模型!!! 流程是没错的,下载master分支编译就可以了
运行
####编译打包镜像的机器 I7-12代
root@85:/home/qwen/Qwen3.5-0.8B-MNN# ../MNN/build/llm_demo config.json ../prompt.txt
The device supports: i8sdot:0, fp16:0, i8mm: 0, sve2: 0, sme2: 0
config path is config.json
main, 267, cost time: 1876.698975 ms
Prepare for tuning opt Begin
Prepare for tuning opt End
main, 275, cost time: 162.406998 ms
prompt file is ../prompt.txt
你好!很高兴认识我。我是 Qwen3.5 的最新版本,由阿里巴巴塔林实验室联合研发。
我的主要特点包括:
- **深度理解语言能力**:具备强大的语义理解和逻辑推理基础。
- **多领域知识积累**:涵盖历史事实、科学常识和伦理道德等多个方面。
- **高效协作能力**:能在代码编写、数据分析等方面提供具体建议。
- **安全合规与责任边界**:
- 严格遵守国家法律法规
- 保护用户隐私和信息安全(如处理个人数据)
- 确保对话的合法性和道德性(避免生成违法、危险或有害内容)
希望这些问题能帮助你更好地理解本系统的功能!😊
#################################
prompt tokens num = 22
decode tokens num = 150
vision time = 0.00 s
pixels_mp = 0.00 MP
audio process time = 0.00 s
audio input time = 0.00 s
prefill time = 0.10 s
decode time = 3.39 s
sample time = 0.05 s
prefill speed = 217.60 tok/s ← 首屏响应
decode speed = 44.18 tok/s ← 生成速度
vision speed = 0.000 MP/s
audio RTF = -nan
##################################
###################### N100上 2B模型 #############################
root@ef1be15091d7:/app# ./bin/llm_demo ./model/config.json p.txt
CPU Group: [ 2 0 3 1 ], 700000 - 3400000
The device supports: i8sdot:0, fp16:0, i8mm: 0, sve2: 0, sme2: 0
config path is ./model/config.json
main, 267, cost time: 9331.930664 ms
Prepare for tuning opt Begin
Prepare for tuning opt End
main, 275, cost time: 572.570984 ms
prompt file is p.txt
你好!我是**Qwen3.5**。
我是通义千问的进阶版本,拥有超大规模上下文窗口(最长256K -token)、视觉/数学等高级能力。在医疗、法律、科研、编程等领域表现强劲。比如我可以帮你拆解复杂的代码逻辑,或是分析专业学术论文的结论...
有什么特别想聊的吗?✨
#################################
prompt tokens num = 21
decode tokens num = 77
vision time = 0.00 s
pixels_mp = 0.00 MP
audio process time = 0.00 s
audio input time = 0.00 s
prefill time = 1.07 s
decode time = 5.88 s
sample time = 0.03 s
prefill speed = 19.70 tok/s
decode speed = 13.10 tok/s
vision speed = 0.000 MP/s
audio RTF = -nan
##################################
root@ef1be15091d7:/app# ./bin/llm_demo ./model/config.json
CPU Group: [ 2 0 3 1 ], 700000 - 3400000
The device supports: i8sdot:0, fp16:0, i8mm: 0, sve2: 0, sme2: 0
config path is ./model/config.json
main, 267, cost time: 7778.529785 ms
Prepare for tuning opt Begin
Prepare for tuning opt End
main, 275, cost time: 818.870972 ms
User: 我分2次给你2个数字,你帮我结算他们的和
A: 请依次告诉我两个数字。
我会把这两个数的总和计算出来:`__ + __ = `
(请直接输入数字,例如:3 和 5)
User: 4
A: 4
User: 6
A: 4 + 6 = 10
User: 很棒!
A: 当然!如果你们接下来还有更多题目需要练习或者有其他问题需要帮忙,随时可以再来问我~加油!😊
User: 我是天天,你以后可以叫我天天
A: 没问题,**天天���**。
那我们就接着上一题的格式来:`4 + 6 = 10`!
如果还需要继续做其他加法,随时吩咐~����
User: 我叫什么?
A: 你的名字是:**天天**。使用了python写了个api方便调用
测试健康:
# 1. 健康检查
curl http://localhost:8456/health
# 2. 测试聊天接口
curl -X POST http://localhost:8456/chat \
-H "Content-Type: application/json" \
-d '{"msg": "你好"}'产物
dockerfile
FROM debian:12-slim
# 环境变量(解决交互/编码问题)
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
# 告诉系统加载 MNN 运行时库
LD_LIBRARY_PATH=/app/lib:$LD_LIBRARY_PATH
# 安装python api运行时依赖
RUN apt update && apt install -y \
python3 python3-pip libomp-dev curl \
&& rm -rf /var/lib/apt/lists/*
# 安装 Flask(OpenAI 接口依赖)
RUN python3 -m pip install --upgrade pip --break-system-packages
RUN pip install flask==2.3.3 --break-system-packages
# 复制 MNN 运行时库 (编译的)
COPY mnn_runtime/lib/libMNN.so /app/lib/
COPY mnn_runtime/lib/libllm.so /app/lib/
COPY mnn_runtime/lib/libMNN_Express.so /app/lib/
# 复制 llm_demo 二进制工具(核心)
COPY mnn_runtime/bin/llm_demo /app/bin/
RUN chmod +x /app/bin/llm_demo # 确保有执行权限
# 创建工作目录 + 权限(避免日志/模型权限问题)
RUN mkdir -p /app/model && \
mkdir -p /app/src && \
mkdir -p /app/logs && \
chmod 777 /app/logs
# 复制应用代码和配置
#COPY src/ /app/src/
COPY .env /app/
# 工作目录 + 暴露端口(你的测试端口 8456)
WORKDIR /app
EXPOSE 8456
# 启动 Flask 服务
CMD ["python3", "/app/src/api_server.py"]docker compose
services:
qwen-service:
build: .
container_name: qwen-mnn
restart: always
ports:
- "8456:8456"
volumes:
- ./model:/app/model
- ./src:/app/src
- ./logs:/app/logs
environment:
- OMP_NUM_THREADS=${OMP_NUM_THREADS}
deploy:
resources:
limits:
cpus: 4.0
memory: 2G
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8456/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60spython flash api
#!/usr/bin/env python3
import os
import time
import subprocess
from flask import Flask, request, jsonify
app = Flask(__name__)
app.config["JSON_AS_ASCII"] = False
# 核心配置
LLM_DEMO = "/app/bin/llm_demo"
CONFIG = "/app/model/config.json"
# 全局变量:仅维护交互式进程
llm_proc = None
# 启动交互式进程(只启动一次)
def start_llm():
global llm_proc
if not llm_proc or llm_proc.poll() is not None:
# 启动交互式模式:llm_demo config.json
llm_proc = subprocess.Popen(
[LLM_DEMO, CONFIG],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf-8",
bufsize=1, # 行缓冲
universal_newlines=True
)
time.sleep(3)
# 初始化进程
start_llm()
# 核心对话接口
@app.route("/chat", methods=["POST"])
def chat():
# 1. 接收用户输入
data = request.get_json()
prompt = data.get("msg", "").strip()
if not prompt:
return jsonify({"code": 1, "msg": "输入不能为空"})
# 2. 确保进程存活
if not llm_proc or llm_proc.poll() is not None:
start_llm()
# 3. 发送输入到 llm_demo
llm_proc.stdin.write(prompt + "\n")
llm_proc.stdin.flush()
# 4. 读取回复(过滤启动日志,只取A:后的内容)
response = ""
while True:
line = llm_proc.stdout.readline()
if not line:
break
# 只处理回复行(A: 开头),跳过其他日志
if line.startswith("A: "):
response += line.replace("A: ", "").strip() + "\n"
# 检测到下一个 User: 说明回复结束
if line.startswith("User:"):
break
# 5. 返回结果
return jsonify({
"code": 0,
"input": prompt,
"reply": response.strip()
})
# 健康检查
@app.route("/health")
def health():
alive = llm_proc and llm_proc.poll() is None
return jsonify({"alive": alive, "pid": llm_proc.pid if alive else None})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8456, debug=False)