☰
单卡DCU跑通Ollama 70B:量化、显存优化与踩坑实战
2026/10/21 22:59:05 网站建设 项目流程

坦白说,最初我也没指望能在“HYGON DCU”这种偏国产算力的卡上跑起来 ollama 70B。毕竟 70B 模型那个体量,过去我都是用两台四卡机器配合张量并行才能跑得舒服,单卡听起来就像开玩笑。但这次手头只有一张 64GB 的 DCU,而且机子还不在数据中心里,我硬着头皮试了一周,结果不仅跑通了,还把量化、上下文长度、并发参数都摸了一遍。这篇文章就把整个过程和踩坑经验完整写出来,给打算在 DCU 上碰 70B 模型的同学一条能直接照做的路线。

本文适合三类人:一是手里有 DCU 或者 ROCm 兼容卡、想跑大模型但不确定能不能跑的人;二是偏好奇国产算力生态、想知道 ollama 在高性能计算卡上的适配情况;三是已经被 70B 模型显存门槛卡住、想找省事方案的人。我会按“先算显存账、再配环境、然后选模型、最后实测调优”的顺序讲,尽量把每个步骤背后的理由都说清楚。

1. 先算一笔账:70B凭什么能塞进一张DCU

1.1 单卡显存是第一步门槛

先泼一盆冷水:70B 模型在 FP16 精度下,光权重就要占大约 140GB 显存。别说是单卡,就算两张 64GB 的 DCU 拼在一起也放不下推理需要的完整状态。

所以“单卡跑 70B”这个目标,从数学上就被限制住了。唯一现实的路径就是量化。我这次用的卡是单卡 64GB 的 HYGON DCU,型号不算新,但关键优势是显存容量够大。量化后的 70B 模型通常能把文件体积压到 40~50GB,再把上下文和运行时余量算进去,刚好能塞进 64GB。

如果手里是 32GB 的 DCU,说实话就别白费劲了,哪怕是 q4_0 这种最激进的量化也放不下完整的 70B 权重,更别说 KV Cache 和运行时开销。

1.2 量化和 KV Cache:给显存做减法

量化是什么?简单说就是把原本用 16 位浮点数存的权重,换成更低的精度来存。70B 模型的参数总量是不变的,但每个参数的存储字节数变小了,整体体积自然也就下去了。

比如 Q4_K_M 这种量化方式,每个权重大约只用 4 到 5 个 bit,比起 FP16 的 16 bit,省了三倍多。代价是模型的理论精度有些损失,但对于对话、内容生成这类任务,体感上损失非常小,基本可以接受。

但权重只是第一部分。推理时还有 KV Cache,也就是模型在生成每个 token 时缓存下来的历史注意力状态。它的体积和上下文长度直接相关。默认 2048 上下文还好,但如果拉到 8192 甚至更多,KV Cache 会吃掉好几个 GB。别小看这几 GB,在 64GB 的卡上,这就是你能不能稳定跑完一篇长文的生死线。

1.3 为什么非要单卡跑而不拆成多卡

可能有人会问:既然一张卡空间不够,为什么不搞多卡并行?答案是 ollama 目前默认不支持多卡张量并行。

我一开始也想用两张卡。但研究后发现,ollama 的 GPU 加载逻辑是“一张卡加载一个模型”。如果你希望它把 70B 模型切到多张卡上并行推理,它做不到。它的并发模型是“同一个模型服务多个用户”,而不是把一层拆到这张卡、另一层拆到那张卡。

所以想要单机多卡跑大模型,老老实实转去用 vLLM 这类专门的推理框架比较靠谱。后面我会单独讲这个替代方案。如果你现阶段就是想快速体验 70B,那么单卡加量化是唯一省事的路。

2. 让ollama识别DCU:环境准备里的那些坑

2.1 驱动与ROCm兼容层的安装顺序

DCU 的软件生态走的是 ROCm 兼容路线。也就是说,很多为 AMD GPU 写的推理框架,理论上经过适配可以跑在 DCU 上。但“理论上”和“实际上”之间,隔着好几个环境变量。

我的安装顺序是这样的:先装好 DCU 对应的内核驱动,确认rocm-smi能正常输出卡的温度、显存和利用率。这一步是地基,如果到这里卡都看不见,后面 ollama 折腾一万年也认不出设备。

然后安装 ROCm 运行时库。这里有个容易踩的坑:ollama 的 ROCm 后端对 ROCm 版本有要求,最好装比较近期的版本。我当时一开始装了个老版本,结果 ollama 日志里总是提示某个动态库找不到,后来升级到 ROCm 6.x 才解决。

装完以后,用rocm-smi和hipcc分别验证驱动和编译器环境。两行命令都正常输出了,再继续。

2.2 两个环境变量让ollama找到卡

ollama 能不能识别 DCU,关键就在环境变量上,具体是两个:

第一个是HSA_OVERRIDE_GFX_VERSION。DCU 的 GPU 架构标识不一定在 ollama 内置的 ROCm 支持名单里。这时候你需要手动指定一个相近的 GFX 版本号来“骗过”运行时检测。我用的值是gfx906或gfx1030,具体看你手上这块卡接近哪代架构。这一步如果不做,ollama 直接显示“no suitable ROCm device found”。

第二个是HIP_VISIBLE_DEVICES。如果机器上有其他 GPU,或者系统里有多张卡,建议显式指定用哪一张。设成HIP_VISIBLE_DEVICES=0就是让 ollama 只看到第一张卡。

启动前我还习惯加一句OLLAMA_LOG_LEVEL=info,这样日志会输出详细的加载过程。跑一遍ollama serve,看到日志里出现类似 GPU 信息成功识别的字样,才算真正通了。

2.3 你大概率会撞上的“识别失败”问题

说实话,第一次跑通之前,我花在环境上的时间比跑模型还多。这里列几个我实际撞到过的问题和解决方式。

第一种是“no suitable ROCm device found”。原因和上面说的 GFX 版本白名单有关,解决方法就是设置HSA_OVERRIDE_GFX_VERSION后重启 ollama。

第二种是驱动识别了卡,但显存读出来是 0。这通常是驱动和 ROCm 版本不匹配导致的。我重装过一次驱动才正常。

第三种比较隐蔽:机器上同时有 NVIDIA 卡。ollama 会优先尝试 CUDA 后端,结果把 DCU 晾在一边。处理方法是设置CUDA_VISIBLE_DEVICES=-1让 CUDA 后端彻底失效,强制 ollama 走 ROCm 路径。

这套环境配好之后,我不建议急着拉 70B。先用一个小模型跑通全流程,比如 7B 或者 13B,确认推理速度正常,再上 70B,排查起来会容易很多。

3. 模型选型和导入:不是越大的量化越好

3.1 70B型号选择:Instruct、Chat与Base

70B 不等于只有一种模型。同样是 70B,Base 版本只能做文本补全,聊起天来像对着一台复读机。Chat 和 Instruct 版本经过指令微调,才能跟着对话走。在 ollama 里拉模型时,一定要看清楚标签。

我这次选的是 Qwen2.5-72B-Instruct,准确说是 72B,比 70B 稍大一点,但两者在显存需求上基本相当。之所以选它,主要是因为 Ollama 官方镜像库里有现成的 GGUF 量化版本,省去自己转换的功夫。如果你选 Llama 3 70B 也可以,但注意不同模型在同一种量化等级下的体积可能有差别,导入前先查一眼模型页面标注的显存需求。

3.2 GGUF量化等级与显存的匹配表

GGUF 是 llama.cpp 家族的标准格式,ollama 对 GGUF 的支持非常成熟。同样一个模型,不同量化等级的文件大小如下,大概参考一下:

量化等级文件大小估算是否适合单卡64GB
Q8_0约 75GB不适合,超显存
Q6_K约 59GB勉强,几乎没有余量
Q5_K_M约 52GB可以,余量紧张
Q4_K_M约 44GB推荐,各方面均衡
Q4_0约 41GB可以,但精度略低

我最终选了 Q4_K_M。原因很直接:它比 Q4_0 文件只大 2~3GB,但推理质量的保留度更好;比 Q5_K_M 小了七八个 GB,这七八个 GB 留给 KV Cache 以后,长文本场景的余量会从容很多。

不要盲目追求最大量化。单卡跑 70B,本身就是一次平衡艺术,显存里既要有模型权重,也要有缓存、还有运行时依赖,每一样都不能缺。

3.3 导入模型前必须算好的显存余量

我把模型导入和启动前实际计算了一下,给新手一个可以直接抄的公式:

  • 模型权重(Q4_K_M):约 44GB
  • 运行时和 llama.cpp 框架开销:约 2~3GB
  • 2048 上下文的 KV Cache:约 1~2GB
  • 系统其他进程:约 1~2GB

加一起大概 49~51GB,单卡 64GB 完全能cover。但如果你把上下文开到 8192,KV Cache 会涨到 4~6GB,这时候内存占用就到 55GB 上下,还算安全。如果开到 16384,我实测下来显存就会破 60GB,开始出现推理变慢甚至卡顿的风险。

所以导入模型前,先想清楚自己最长要处理多长的文本。如果只是普通对话,2048 完全够;要是让模型读论文或者长代码,4096 是稳妥上限。

4. 首次启动与实测表现:从拉模型到跑通一遍

4.1 启动参数与第一轮对话

环境变量设置好之后,启动命令非常简单:

export HSA_OVERRIDE_GFX_VERSION=gfx1030 export HIP_VISIBLE_DEVICES=0 export OLLAMA_LOG_LEVEL=info export OLLAMA_CONTEXT_LENGTH=4096 ollama run qwen2.5-72b-instruct:q4_k_m

OLLAMA_CONTEXT_LENGTH=4096是我比较推荐的起步值。这个值是改的 ollama 服务端默认上下文长度,不是模型本身固定死的,运行时还可以针对不同请求覆盖。

第一轮对话我至今印象深刻:我输入“你好,请介绍一下你自己”,等了大约五六秒,首 token 才缓慢蹦出来。但那不是故障,70B 模型的首 token 延迟本来就在好几秒量级。关键是接下来生成是稳定的,不是挤牙膏式的一字一卡。

我用 Python 直接调用/api/generate接口做了个简单压力测试,脚本逻辑是连续发十次请求,每次让模型生成一段约 300 token 的内容,统计每次的端到端耗时。

import requests import json import time url = "http://localhost:11434/api/generate" payload = { "model": "qwen2.5-72b-instruct:q4_k_m", "prompt": "请写一段关于人工智能发展趋势的短文,约300字。", "stream": False } total_tokens = 0 total_time = 0.0 for i in range(10): start = time.time() resp = requests.post(url, json=payload) data = resp.json() total_tokens += data.get("eval_count", 0) total_time += time.time() - start print(f"第{i+1}次请求完成") print(f"平均每秒生成token数: {total_tokens / total_time:.2f}")

这个脚本原理很简单:拿到每次返回的eval_count(本次生成的 token 数量)和总耗时,两者相除就是生成速度。注意要保证每次请求的生成量差不多,否则平均值会被误导。

4.2 实测速度数据与体感

同样是 70B 级别模型,在 DCU 单卡上,ollama的实测表现到底如何,我得到的数字如下:

测试指标实测结果
首 token 延迟4~8 秒
稳定生成速度5~9 token/s
10 次连续请求稳定性无一次 OOM,速度波动不大
4096 上下文下显存占用约 55~58GB

5~9 token/s 是什么概念?人眼阅读中文内容,每秒大概能看 10~20 个字,而中文一个 token 大约对应 1~1.5 个字。所以模型生成的速度,已经接近甚至略慢于人读的速度。用来写长文档、做总结,可以把请求挂后台,然后去喝杯水回来拿结果。但如果拿来做实时聊天机器人,体感会比较着急,因为每句话都要等好几秒。

有一点值得夸:连续跑了二十分钟,显存利用率一直稳定在 90% 左右,没有出现过一次 OOM 或者进程崩溃。这在国产算力卡上算是很让人安心的表现。

4.3 长上下文测试中的显存变化

跑通短对话之后,我又试了试长文。让模型读一份约 6000 字的资料,再基于它做总结。刚开始上下文 4096,没有问题。我把OLLAMA_CONTEXT_LENGTH改到 8192 再启动,显存占用立刻飙到接近 60GB,首 token 延迟也明显变长。

继续尝试 16384,这次就露馅了。加载完模型之后,模型直接报显存不足,进程自动退出。所以对于单卡 DCU 跑 70B,我建议把上下文上限死死摁在 4096。如果你一定要处理更长文本,更好的方案是先用小模型或者外部工具把文本切块,而不是强行和显存较劲。

5. 还能再快一点吗:调优与替代工具

5.1 ollama侧的参数调整

先说说不用换框架就能做的优化。第一是OLLAMA_NUM_PARALLEL。这个参数控制同一个模型同时处理几个请求。对 70B 这种大模型,我建议设成 1,也就是一次只处理一个请求。表面上看线程并发变小了,但因为减少了显存压力和缓存冲突,实际单请求的速度反而稳定。

第二是OLLAMA_KEEP_ALIVE。模型加载一次就要花不少时间,如果频繁切换模型或者隔几分钟不用,ollama 可能会把模型从显存里卸载。设成一个较大的值,比如OLLAMA_KEEP_ALIVE=1h,可以让模型在显存里多待一会儿。

第三是批处理长度。ollama 内部有个 batch size 的默认值,对长文本生成影响不小。实测下来,适当增大 batch size 能把吞吐提高一点儿,但代价是首 token 延迟变高。这个要根据实际场景取舍:交互式对话希望首 token 快,长文生成希望总 throughput 高。

5.2 单卡极限的替代方案:vLLM张量并行

如果你不满足于 5~9 token/s,想要接近二三十 token/s 甚至更高的吞吐,那 ollama 确实到头了。真正的出路还是 vLLM。

vLLM 天然支持多卡张量并行,尤其适合 DCU 这种 ROCm 兼容环境。它的部署思路不同:不是加载 GGUF 文件,而是加载传统的 safetensors 格式权重。我试过用两张 DCU 张量并行跑同一个 72B 模型,速度确实比单卡量化快了一个量级,而且因为权重是 FP16,输出质量也更稳定。

但代价是配置复杂度上了一个层次。

要用 vLLM 跑通,有三个额外步骤:权重格式转换、张量并行参数设置、服务启动参数调整。

python -m vllm.entrypoints.openai.api_server \ --model /path/to/model \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.92 \ --max-model-len 4096 \ --enforce-eager

--tensor-parallel-size 2就是让两张卡协作处理同一个模型。--gpu-memory-utilization 0.92表示允许 vLLM 用掉单卡 92% 的显存。注意如果卡数不对或者驱动有问题,它会直接报 device 数量不匹配。

vLLM 还有一个好处是并发能力强,多个请求同时进来时,它会自动排队和批处理,整体 GPU 利用率比 ollama 高不少。如果你有正式业务得接,建议花一个周末把 vLLM 路线啃下来。

5.3 什么场景下这套组合真正值得用

折腾完这一圈,我自己的想法也清晰了。单卡 DCU 加 ollama 跑 70B,适合的场景有三类。

第一类是项目验证。你正准备采购算力设备,想先确认 DCU 跑主流大模型没问题,这套方案成本最低,一个周末就能给出结论。

第二类是离线生成任务。比如批量写报告、批量翻译、离线代码审查。这些任务不要求秒回,5~9 token/s 完全够用。你只需要写个脚本把任务列表扔进去,然后等待结果。

第三类是小规模内部服务。几个人用,同时并发请求不超过两三个,OLLAMA_NUM_PARALLEL=1配合排队,可以稳定跑一整天。

不适合的场景也很明确:高并发在线服务、严格低延迟交互、需要处理超长文档的应用。这些情况老老实实上多卡 vLLM,或者干脆考虑云端 API。

根据我个人经验,最后再提醒一个细节:DCU 在持续高负载下的散热和稳定性表现,和 NVIDIA 卡不完全一样。跑长任务时建议留意一下rocm-smi里的温度读数,如果长期超过八个九度,通风就得跟上。另外官方仓库里的 GGUF 模型版本偶尔会更新,升级版本前记得确认新量化的体积变化,别悄悄超了显存。这套配置我跑了快一个月,除了上下文长度踩过坑,整体稳定性是过关的,你可以放心照步骤抄作业。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询