我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Qwen千问大模型Ubuntu本地部署实战:从硬件评估到API服务

Qwen千问大模型Ubuntu本地部署实战:从硬件评估到API服务 最近在模拟项目X里需要把千问大模型放到一台 Ubuntu 服务器上做成一个能被其他业务系统调用的本地服务。整个流程走下来比想象中繁琐但也比想象中可控。只要你按顺序把硬件、环境、权重、服务和排错这几关过掉千问大模型并没有多神秘。这篇文章就是我完整部署的记录适合有一定 Linux 基础、但没接触过大模型推理落地的开发者。照着实操基本能复现。先说结论本地部署千问模型最核心的约束是显存。CPU、内存和磁盘不够的时候通常只是跑得慢、启动慢显存不够模型直接加载不起来。所以动手之前一定要先认清自己的硬件底子。1. 部署前先摸清家底硬件与系统准备1.1 门槛到底在哪里千问模型不是一个单独的软件包而是一系列不同参数量级的开源模型。日常用得最多的是 Qwen2.5 系列的 Instruct 版本也就是经过指令微调、可以直接对话的版本。参数越大的模型能力越强但占用的显存也越高。先给出一张按经验整理的显存参考表。注意这里的“占用”不是模型文件大小而是加载到显卡后权重加上基本 KV Cache、激活值的大致占用。模型档位参数量FP16/BF16 权重占用Int4 量化占用建议显存Qwen2.5-0.5B5 亿约 1~2 GB约 0.8 GB2 GB 以上Qwen2.5-1.8B18 亿约 4 GB约 1.2 GB4 GB 以上Qwen2.5-7B70 亿约 15 GB约 5 GB8 GB 以上量化/ 16 GB 以上非量化Qwen2.5-14B140 亿约 28 GB约 10 GB12 GB 以上量化/ 32 GB 以上非量化Qwen2.5-32B320 亿约 65 GB约 20 GB24 GB 以上量化/ 多卡Qwen2.5-72B720 亿约 145 GB约 40 GB多卡或大内存加量化这里要解释一个常见误区显存占用不是只看模型权重。模型推理时每一层的激活值、KV Cache 都会占据显存而且 KV Cache 会随着输入长度的增加而变大。所以表格里的数字只是“权重部分”的估算实际部署时最好把上下文长度控制在合理范围否则 7B 模型在 16 GB 显存上也可能触发 OOM。内存建议 32 GB 起步。如果只是跑 0.5B、1.8B 这种小模型16 GB 内存也能接受但跑 7B 以上内存太小会在模型加载阶段就卡住因为很多权重文件要先从磁盘读到内存再拷贝到显存。磁盘建议预留 NVMe SSD模型分片文件较大机械硬盘加载模型会让人怀疑程序是不是卡死了。1.2 Ubuntu 系统层面的准备工作部署千问模型我推荐 Ubuntu 而不是 Windows原因很简单CUDA 生态、Python 包兼容性、后台进程管理在 Linux 上更顺畅。尤其是把模型做成 API 服务后用 systemd 管理进程比 Windows 服务要直观得多。第一步是确认显卡和驱动。终端执行lspci | grep -i nvidia nvidia-smi如果nvidia-smi能正常输出说明驱动已就绪。右上角会显示驱动版本号和 CUDA Version这个 CUDA Version 是指当前驱动支持的最高 CUDA 版本不是一定已经装好的 CUDA Toolkit。驱动没装的话最直接的方式是sudo apt update sudo apt install nvidia-driver-535 sudo reboot驱动分支很多535 只是一个常用示例。不同显卡适合不同驱动分支装完以后务必用nvidia-smi确认。驱动安装是我建议放在最前面的原因因为它最容易影响后续所有环节。驱动版本过低PyTorch 新版本可能找不到对应的 CUDA 算子驱动升级过猛又可能让老显卡失去支持。接下来安装 CUDA Toolkit。这里多说一句很多人误以为装驱动就够了其实跑 Python 大模型时PyTorch 会带自己的 CUDA runtime不装 Toolkit 也能跑。但本地编译一些高性能算子时需要nvcc所以还是建议装。sudo apt install nvidia-cuda-toolkit nvcc -V给nvcc配置环境变量写入~/.bashrcexport PATH/usr/local/cuda/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda/lib64:$LD_LIBRARY_PATH source ~/.bashrc系统层面还要装一些基础工具sudo apt install git tmux curl htop lsof -y其中tmux我强烈建议安装。下载模型权重动辄几十 GB中途断连或关闭终端都会导致下载失败。用tmux new -s qwen_download开一个会话把下载任务放进去就算 SSH 断开重连之后还能看到任务继续能省掉不少重复劳动。2. Python 环境与依赖安装地基要打牢2.1 隔离环境别污染系统 PythonUbuntu 20.04 和 22.04 自带 Python 3.8 或 3.10很多系统工具依赖这些 Python。不要直接在系统 Python 里pip install大模型相关依赖容易把环境搞乱而且某些包升级后会导致系统命令异常。我习惯用 Miniconda 创建独立环境。Miniconda 安装包不大安装后执行conda create -n qwen python3.11 -y conda activate qwen python --versionPython 版本我推荐 3.10 或 3.11。3.12 虽然新但很多大模型推理库和自定义算子编译在 3.12 上容易出兼容问题。为了少踩坑3.11 是当前比较保险的选择。为什么一定要隔离因为大模型部署里最难排的问题往往不是模型本身而是包版本互相污染。举个例子系统 Python 里已经有一个旧版 NumPytransformers 新版要求更高版本升级 NumPy 又可能导致另一个系统工具不可用。conda 环境把这一切隔离开环境坏了就删掉重建不用折腾系统。2.2 依赖版本到底怎么选创建好环境后先装 PyTorch。这一步最容易出错因为 PyTorch 的 CUDA 版本必须和驱动匹配。不要用普通 PyPI 默认命令直接装建议到 PyTorch 官网选择对应 CUDA 版本的安装命令。以 CUDA 12.1 为例常见命令是pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121装完以后验证一下import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果torch.cuda.is_available()返回 False多半是装成了 CPU 版本。检查torch.version.cuda如果为空卸载重装 GPU 版本。这一步验证能节省后续大量排查时间。接着装模型推理相关的依赖pip install transformers4.43 accelerate einops sentencepiece这几个包的作用要清楚transformers 负责加载模型、跑推理accelerate 负责在 GPU、CPU、多卡之间自动分配模型层einops 用于张量重排sentencepiece 是中文分词器的重要依赖。缺少最后一个很多模型加载时会报 tokenizer 相关错误。如果需要做 4bit 量化再加pip install bitsandbytesFlashAttention 可以先不装。不少新手一上来就想装 flash-attn结果在编译阶段卡几个小时。实际上普通推理用默认 Attention 也能跑只是生成速度略慢一些。先把流程跑通再考虑性能优化。3. 模型权重获取与本地加载把“大脑”请回家3.1 选哪个规格目录里都有什么第一次部署不建议直接上最大模型。如果只是想体验千问的对话能力从 Qwen2.5-7B-Instruct 开始是最合适的。7B 模型在能力与硬件成本之间比较平衡量化后 8 GB 显存也能跑。如果显卡只有 4 GB可以先从 0.5B 或 1.8B 开始。模型权重不是单个文件而是一个目录。下载完成后目录里一般包含这些文件config.json模型结构超参数比如层数、隐藏层维度、词表大小。tokenizer.json和tokenizer_config.json分词器负责把文本切成 token。generation_config.json生成默认参数比如温度、重复惩罚。model.safetensors或model.safetensors.index.json模型权重文件通常被切成多个分片文件体积很大。下载权重时我建议单独建一个数据目录不要放在 home 目录里。比如/data/models/Qwen2.5-7B-Instruct。模型文件动辄十几 GB后续升级、迁移、换版本时独立目录会方便很多。从模型托管平台下载时优先用平台提供的命令行工具可以一键拉取整个目录。手动在网页里一个个点文件下载容易漏分片。下载完成后对比一下目录文件大小和平台标注的大小确认没缺文件再继续下一步。3.2 用 transformers 加载模型的正确姿势在 conda 环境里写一个 Python 脚本加载模型以本地目录方式为例import torch from transformers import AutoModelForCausalLM, AutoTokenizer model_dir /data/models/Qwen2.5-7B-Instruct tokenizer AutoTokenizer.from_pretrained(model_dir, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypetorch.float16, device_mapauto, trust_remote_codeTrue )torch_dtypetorch.float16表示把模型权重以半精度加载显存占用大约是 FP32 的一半同时还能利用显卡上的 Tensor Core 加速。大多数千问模型在推理时用 FP16 就可以了效果与 BF16 相差不大。device_mapauto是给 accelerate 用的参数。它会自动判断有多少 GPU、每张卡剩余多少显存把不同层放到合适的位置。如果显存不够还会自动把部分层放到内存里牺牲速度换取不报错。单卡、多卡都能用这个参数。trust_remote_codeTrue是个需要留意的选项。有些模型目录里带了自定义的 Python 代码transformers 需要执行这些代码才能正确加载模型。这个参数只应在可信模型目录上使用不确定来源的权重不要随便开。3.3 量化加载和显存检查当显存不够而模型能力又不想降档时可以用量化。量化可以理解为把高清图片压成适合传输的格式体积变小画质有轻度损失。大模型量化同理把 FP16 权重压缩成 Int8 或 Int4显存占用大幅下降输出质量会有轻微变化但大多数时候可接受。使用 bitsandbytes 做 4bit 量化加载model AutoModelForCausalLM.from_pretrained( model_dir, torch_dtypetorch.float16, device_mapauto, load_in_4bitTrue, bnb_4bit_compute_dtypetorch.float16, trust_remote_codeTrue )量化加载第一次跑会比较慢因为要做权重格式转换。如果中途报 bitsandbytes 相关错误先确认它和 CUDA 版本是否匹配。常见情况是驱动版本比较旧时新版 bitsandbytes 的 CUDA 算子无法加载需要选对应旧版本重装。模型加载成功后可以执行下面几行确认显存占用free_mem, total_mem torch.cuda.mem_get_info() print(ffree: {free_mem / 1024**3:.2f} GB, total: {total_mem / 1024**3:.2f} GB)如果剩余显存连 1 GB 都不到说明上下文长度开不了太大后续生成长文本容易 OOM。此时要么换更小的模型要么启用量化。4. 跑通本地推理与对外服务4.1 先做一次最小推理验证模型加载成功只是第一步真正让它回答问题需要走一遍“拼提示词 → 分词 → 生成 → 解码”的流程。这里最关键的细节是聊天模板。现在的对话模型都经过指令微调输入格式必须符合它训练时的聊天模板。直接写“你好”丢给模型效果会比较差。正确写法是用 tokenizer 的apply_chat_templatemessages [ {role: system, content: 你是一个乐于助人的中文助手。}, {role: user, content: 用一句话解释什么是大语言模型。} ] text tokenizer.apply_chat_template( messages, tokenizeFalse, add_generation_promptTrue ) model_inputs tokenizer([text], return_tensorspt).to(model.device) generated_ids model.generate( **model_inputs, max_new_tokens512, do_sampleFalse ) response tokenizer.decode( generated_ids[0][model_inputs.input_ids.shape[1]:], skip_special_tokensTrue ) print(response)max_new_tokens控制生成的最大长度不是总长度。在显存有限时不要把值设成 4096因为每生成一个 tokenKV Cache 都会增长越到后面显存压力越大。do_sampleFalse采用贪心解码输出更确定如果想看更多样化的回答可以改成do_sampleTrue, temperature0.7, top_p0.8。首次生成通常比后续调用慢因为 CUDA kernel 需要预热模型权重也要完成第一次完整加载。这不代表程序有问题多测几次速度会回到正常水平。4.2 把模型变成可以被调用的 API 服务本地脚本能跑通只是完成了验证。实际业务里外部系统不会直接 import 你的 Python 环境而是通过 HTTP 接口调用模型。千问官方仓库里提供了标准 Chat Completions 接口的 API 服务脚本。把仓库代码克隆到本地后启动命令大致是python openai_api_demo.py \ --model /data/models/Qwen2.5-7B-Instruct \ --server-name 0.0.0.0 \ --server-port 8000用0.0.0.0监听时允许局域网内其他机器访问。如果只想本机访问就用127.0.0.1。除了官方脚本也可以用 vLLM 提供的 API Server。vLLM 的优势是并发吞吐更高适合正式服务。基础启动参数类似指定模型路径和端口即可python -m vllm.entrypoints.openai.api_server \ --model /data/models/Qwen2.5-7B-Instruct \ --port 8000 \ --gpu-memory-utilization 0.85--gpu-memory-utilization 0.85表示在推理期间最多使用 85% 的显存留一部分给 CUDA context 和临时张量。不要设成 0.95生成长文本时比较容易触发 OOM。这里必须提醒本地 API 服务默认没有鉴权。如果服务暴露到公网任何人都可以调用显卡会被占满资源可能被耗尽。内网使用也要在网关上做统一鉴权或者在上游入口加认证校验。4.3 用 curl 请求测试服务启动后打开新的终端用 curl 做一次请求curl http://127.0.0.1:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: Qwen2.5-7B-Instruct, messages: [{role: user, content: 你好请介绍一下你自己}], max_tokens: 512, temperature: 0.7 }返回结果里choices[0].message.content就是模型生成的回答usage字段会显示 prompt_tokens、completion_tokens 等 token 统计信息。先不加stream: true等基础流程通顺后再测试流式返回能减少排查复杂度。更常见的做法是在应用层写一个 Python 客户端import requests url http://127.0.0.1:8000/v1/chat/completions payload { model: Qwen2.5-7B-Instruct, messages: [ {role: system, content: 你是一个编程助手。}, {role: user, content: 请写一段 Python 快速排序} ], temperature: 0.3, } resp requests.post(url, jsonpayload, timeout120) data resp.json() print(data[choices][0][message][content])如果想把这个 API 服务变成开机自启、崩溃自动重启的常驻进程建议用 systemd。创建服务文件/etc/systemd/system/qwen-api.service[Unit] DescriptionQwen API Service Afternetwork-online.target [Service] Userdev WorkingDirectory/data/qwen ExecStart/home/dev/miniconda3/envs/qwen/bin/python /data/qwen/openai_api_demo.py --model /data/models/Qwen2.5-7B-Instruct --server-name 127.0.0.1 --server-port 8000 Restarton-failure RestartSec5 EnvironmentCUDA_HOME/usr/local/cuda [Install] WantedBymulti-user.target注意ExecStart里用了 conda 环境的绝对路径 Python。如果不写绝对路径systemd 在启动时不会自动加载 conda 环境可能找不到 transformers 包。启用服务sudo systemctl daemon-reload sudo systemctl enable --now qwen-api journalctl -u qwen-api -f5. 常见问题与排查实录5.1 问题与解决速查表部署过程中遇到的问题大部分集中在环境匹配、显存不足、文件缺失这三类。我整理了一张排查表按症状查原因比从头翻日志要快。症状可能原因处理办法报错CUDA out of memory模型权重或 KV Cache 超出显存换小模型、启用量化、调低max_new_tokens、调低gpu-memory-utilization加载时报No such file or directory模型路径不对或权重分片缺失确认model_dir指向权重目录检查索引文件列出的全部分片是否存在报Model not supportedtransformers 版本太旧不认识该模型结构升级transformers到 4.43 以上加载报错提示需要trust_remote_code模型目录里包含自定义 Python 代码确认目录可信后加上trust_remote_codeTruetorch.cuda.is_available()返回 FalsePyTorch 装成了 CPU 版本重装 GPU 版本检查torch.version.cuda端口被占用上次服务进程未退出lsof -i:8000找到进程kill 后重启生成内容为空解码时截断位置不对按model_inputs.input_ids.shape[1]截断生成结果并设置skip_special_tokensTrue生成速度明显变慢部分层被 offload 到 CPU降低模型规格或换显存更大的显卡5.2 三个绕不开的坑第一个坑是一上来就折腾 FlashAttention。FlashAttention 确实能提升推理效率但它需要针对具体 GPU 架构和 CUDA 版本编译过程比较痛苦。在核心流程没跑通之前不需要它。先让模型正常生成再回来优化这是更明智的顺序。第二个坑是依赖包来源混用。有人一会儿sudo pip install一会儿conda install最后导致同一个包在系统环境和 conda 环境里各装了一份版本不一致报错时很难定位。我自己的习惯是所有 Python 包都在 conda 环境里用pip安装系统包用apt两者互不交叉。第三个坑是权重大文件重复下载。模型文件动辄十几 GB下载两次就是双倍时间和带宽。不同项目复用一个模型时不要手动复制一份权重目录应该建一个公共目录比如/data/models/小项目直接引用这个路径。后续升级新版本也是在新目录里下载完整权重确认可用后再把业务指向切换过去。6. 性能调优与后续扩展6.1 让生成更快推理引擎怎么选默认的 transformers 推理跑通没问题但生成速度有瓶颈。大模型是逐 token 生成的每生成一个 token都要把模型权重读一遍显存带宽成了最大的制约。单卡上 7B 模型用 transformers 跑生成速度可能只有每秒十几个 token并发多几个请求时延迟会明显上升。想提升吞吐常见方案有三个方案适合场景特点Transformers 默认加载快速验证、原型开发配置简单通用性好并发吞吐一般vLLM高并发 API 服务支持 PagedAttention、连续批处理吞吐明显提升但对驱动和 Python 版本要求更严格llama.cppCPU/GPU 混合、小显存机器使用 GGUF 量化格式内存占用低方便在没有 N 卡的机器上跑如果公司有正式的内部服务需求我建议最终切到 vLLM。它处理多个并发请求的方式比 transformers 高效很多尤其适合多人同时调用。切换之前先用前面的最小推理脚本把模型路径、接口协议验证好再换 vLLM 会顺利很多。如果显卡比较弱或者只有集显可以考虑 llama.cpp 配合 GGUF 格式的量化模型。它能在 CPU 上跑也能把一部分层放到 GPU 上灵活性很好只是部署时多一步格式转换或专门下载 GGUF 文件。6.2 生产环境还要补哪些课模型服务稳定跑起来之后不要以为就结束了。显存和 GPU 利用率需要监控。简单做法是用nvidia-smi --query-gpumemory.used,utilization.gpu --formatcsv -l 1也可以安装nvtop交互式查看每张卡的实时状态。当显存持续接近上限时说明上下文长度或并发数设置偏高需要调低max-model-len或限制并发数。日志方面如果 API 服务跑在 systemd 下默认日志会不断写入 journal时间久了会占用不少磁盘空间。可以在/etc/systemd/journald.conf里设置日志容量上限或者把应用日志单独交给日志轮转工具管理。模型更新也要有条理。大模型权重不是频繁更新的软件但每出一个新版本更新前都应该先在测试环境跑一轮回归测试。固定准备几十个问题比较新旧版本的回答质量、生成速度和显存占用确认没有明显回退后再切换。切换时不要直接覆盖旧权重目录用软链接指向新版本目录出问题能快速回滚。最后说一个适合继续往下扩展的方向检索增强生成。千问模型本身的知识有截止日期企业内部文档、本地知识库它并不知道。可以先用一个嵌入模型把文档切块、向量化等用户提问时检索相关片段再把这些片段拼到提示词里交给千问。这样的回答既借助了千问的生成能力又能结合本地资料很多内部知识库就是按这个思路落地的。回顾整次部署我最大的体会是大模型部署的难点不在模型本身而在环境一致性。驱动版本、CUDA 版本、PyTorch 版本、依赖包版本任何一环对不上都会报出让人摸不着头脑的错误。动手前先把版本写清楚每装一个重包就做一次最小验证会顺利很多。最后再分享一个小技巧把每次安装命令和输出按日期存成日志文件部署中断隔天再继续时能省掉大量重复排查工作。
返回列表