1
1
DCU技术达人
新人贴
我在海光 BW 64GB 上跑 Qwen2.5-0.5B:一次离线搬运、BF16 推理和批处理实测
2026-09-28 17:59:05
9
# 我在海光 BW 64GB 上跑 Qwen2.5-0.5B:一次离线搬运、BF16 推理和批处理实测 https://www.scnet.cn/ui/console/index.html#/notebook/tool-panel/monitor?id=2082711171478953986&clusterId=20091&taskId=2082772159850979329&name=2607301413327275 ![1790589378016.png](/api-forum/file/RedirectImage?path=20260928/1790589399045_1790589378016.png '1790589378016.png') 拿到这台海光实例时,我原本准备直接上 1.5B 模型。下载任务启动后我才意识到顺序反了:节点访问 ModelScope 和 PyPI 都超时,我还没确认权重能不能顺利加载,就要先等半小时搬一个更大的模型。 我停掉了那次下载,换成 Qwen2.5-0.5B-Instruct。理由很现实——当时要验证的是软件栈和交付路径,不是模型回答上限。一个不到 1GB 的快照,足够把 ModelScope 下载、离线传输、SHA256 校验、BF16 推理和显存统计全部走一遍。链路确认没问题,再换大模型也不迟。 最后我不但跑通了单请求,还补测了批量为 1、2、4、8 时的固定长度生成。批量为 8 时,聚合吞吐达到 212.50 token/s,峰值显存不到 1GiB。与此同时,0.5B 模型也当场给了我一个教训:它生成的 JSON 可以通过语法解析,字段含义却可能是错的。 这篇记录的重点就在这两面——海光环境跑标准 Qwen 权重没有障碍,小模型输出则必须带着怀疑去验。 > 所有数字来自同一台实例上的实际日志。为便于横向比较,批处理补测强制每条生成 64 个新 token;它是吞吐测试,不是产品峰值,也不等于真实对话延迟。 ## 我先排除的不是模型问题,而是网络问题 节点的基础资源其实相当宽裕:系统是 Ubuntu 22.04.5 LTS,容器有 15 核 CPU 配额、约 59GiB 内存,`torch.cuda.get_device_name(0)` 返回 `BW`,设备显存约 63.98GiB。 真正卡住我的第一件事是网络。外部 HTTPS 请求直接超时,远端也没有 `modelscope` 命令。这种情况下继续在节点里折腾 `pip install` 没什么意义,因为安装工具和下载权重都会碰到同一道墙。 我最后采用的分工是: ```text 本地可联网电脑:安装 ModelScope、下载模型、计算哈希 海光计算节点:接收完整快照、复核哈希、离线加载和推理 ``` 这不是什么高级技巧,但很像真实内网环境里的模型交付。计算节点不需要长期访问模型社区,模型版本也能由目录和哈希固定下来。 我当时记录的环境如下: | 项目 | 实测值 | | ------------ | ------------------ | | 操作系统 | Ubuntu 22.04.5 LTS | | CPU 配额 | 15 核 | | 内存上限 | 约 59GiB | | 加速设备 | Hygon BW | | 设备显存 | 63.98GiB | | Python | 3.10.12 | | PyTorch | 2.10.0 | | Transformers | 5.5.0 | | HIP | 6.3.26093 | | 推理精度 | BF16 | 模型来自 ModelScope: [https://modelscope.cn/models/qwen/Qwen2.5-0.5B-Instruct](https://modelscope.cn/models/qwen/Qwen2.5-0.5B-Instruct) 模型页标注约 494.03M 参数、快照约 999.60MB,许可证为 Apache-2.0。 ## 模型是怎样搬过去的 我在本地单独建了一个虚拟环境,没有污染平时使用的 Python: ```powershell python -m venv .runtime/modelscope-venv .\.runtime\modelscope-venv\Scripts\python.exe ` -m pip install modelscope ``` 随后把快照下载到明确目录,而不是依赖默认缓存: ```powershell .\.runtime\modelscope-venv\Scripts\modelscope.exe download ` --model Qwen/Qwen2.5-0.5B-Instruct ` --local_dir .\models\Qwen2.5-0.5B-Instruct ``` 下载完成后共有 11 个文件,目录约 954MiB。真正花时间的是 `model.safetensors`,所以我只对这个核心文件做双端 SHA256: ```powershell Get-FileHash ` .\models\Qwen2.5-0.5B-Instruct\model.safetensors ` -Algorithm SHA256 ``` 远端对应命令: ```bash sha256sum \ /root/qwen-modelscope-labs/models/Qwen2.5-0.5B-Instruct/model.safetensors ``` 两边得到的值完全一致: ```text fdf756fa7fcbe7404d5c60e26bff1a0c8b8aa1f72ced49e7dd0210fe288fb7fe ``` ![1790589407286.png](/api-forum/file/RedirectImage?path=20260928/1790589428296_1790589407286.png '1790589407286.png') 我一直保留这一步,因为“目录大小差不多”并不能证明大文件传输完整。尤其是模型反序列化失败时,先确认哈希能省掉大量错误方向。 ## `torch.cuda` 为什么会显示海光 BW 第一次看示例代码时,`model.to("cuda")` 这个写法让我停顿了一下。设备明明不是 NVIDIA,为什么仍然叫 `cuda`? 这里的名字来自 PyTorch 兼容接口,不能靠函数名判断底层硬件。我用几行输出把事实确认下来: ```python import torch print("available:", torch.cuda.is_available()) print("torch:", torch.__version__) print("hip:", torch.version.hip) print("device:", torch.cuda.get_device_name(0)) print( "memory:", torch.cuda.get_device_properties(0).total_memory, ) ``` 实际输出里,设备名是 `BW`,`torch.version.hip` 为 `6.3.26093`。也就是说,上层仍沿用 PyTorch 熟悉的设备 API,底层由当前环境里的 HIP/DTK 构建负责。 这对迁移现有代码很重要。我不需要为了识别设备把整个 Transformers 脚本重写一遍,真正需要确认的是 PyTorch 构建版本、HIP 运行时和设备名是否对应。 ## 第一轮推理:跑得很轻,但回答不能照单全收 核心加载代码很普通。我特意加了 `local_files_only=True`,这样即使配置缺文件,程序也会直接报本地错误,不会在后台等待外网。 ```python from transformers import ( AutoModelForCausalLM, AutoTokenizer, ) import torch model_dir = ( "/root/qwen-modelscope-labs/models/" "Qwen2.5-0.5B-Instruct" ) tokenizer = AutoTokenizer.from_pretrained( model_dir, local_files_only=True, ) model = AutoModelForCausalLM.from_pretrained( model_dir, torch_dtype=torch.bfloat16, local_files_only=True, ).to("cuda") model.eval() ``` 计时时我在生成前后都调用了 `torch.cuda.synchronize()`。不加同步点,CPU 端记录到的只是异步任务提交时间,数字会好看,却没有可比性。 我准备了三种中文输入:巡检记录归纳、会议记录转 JSON、设备接口解释。结果如下: | 用例 | 输入 token | 输出 token | 延迟 | 生成速度 | | --------------- | ---------: | ---------: | ------: | ------------: | | 巡检记录归纳 | 147 | 128 | 3.810s | 33.60 token/s | | 会议记录转 JSON | 81 | 82 | 2.444s | 33.55 token/s | | 设备接口解释 | 64 | 27 | 0.815s | 33.14 token/s | | 三项平均 | — | — | 2.3562s | 33.43 token/s | 模型加载用了 1.202 秒,加载后分配显存约 0.9202GiB,本轮观察到的峰值约 0.9582GiB。 ![1790589431683.png](/api-forum/file/RedirectImage?path=20260928/1790589452669_1790589431683.png '1790589431683.png') 如果只看这一张表,很容易写成“模型部署成功、回答正常”。我当时却对第二条结果很不满意:原文的截止时间是“周五”,模型擅自换成了一个具体日期,还添加了原文没有的任务状态。第三条关于 `torch.cuda` 的解释也过于简化。 硬件完成了它应该完成的计算,但模型内容质量没有因此自动变好。这条边界必须分清。 ## 我给结构化输出补了一道语义闸门 最初的验证程序只有 `json.loads()`。模型生成的内容语法正确,它就会被记作成功。看到虚构日期后,我把验证改成了两层: ```python import json import re def extract_json(text: str) -> dict: match = re.search(r"\{.*\}", text, flags=re.DOTALL) if not match: raise ValueError("answer does not contain JSON") value = json.loads(match.group(0)) if not isinstance(value, dict): raise TypeError("answer must be an object") return value parsed = extract_json(model_answer) if parsed != expected: raise ValueError("semantic gate rejected the answer") ``` 两条严格抽取样例都能被 JSON 解析器接受,但只有一条字段和值完全匹配。另一条把 `owner`、`deadline`、`task` 错写成了一个 `keyOrder` 字段。 ![1790589466026.png](/api-forum/file/RedirectImage?path=20260928/1790589487018_1790589466026.png '1790589466026.png') 这次失败改变了我对小模型的用法。现在我更愿意让它“提出候选结果”,再由代码决定是否接收: ```text 生成结果 -> JSON 解析 -> 字段白名单 -> 类型检查 -> 与原文值核对 -> 不通过则重试或切换更大模型 ``` 只要涉及写数据库、改配置或触发自动化动作,我不会把“能解析”当成“可信”。 ## 补测批处理:批量为 8 时,吞吐明显上来了 单请求跑通后,我又加了一组固定长度实验。每条请求强制生成 64 个新 token,先预热一次,再对每个批量重复三轮。这样做牺牲了一部分真实对话特征,但不同批量之间更容易比较。 测试代码的关键部分是: ```python for batch_size in (1, 2, 4, 8): encoded = tokenizer( prompts[:batch_size], return_tensors="pt", padding=True, ).to("cuda") with torch.inference_mode(): output = model.generate( **encoded, min_new_tokens=64, max_new_tokens=64, do_sample=False, ) ``` 实测矩阵如下: | 批量 | 平均延迟 | 聚合吞吐 | 请求吞吐 | 峰值显存 | | ---: | -------: | -------------: | ----------: | --------: | | 1 | 2.0805s | 30.76 token/s | 0.481 req/s | 0.9647GiB | | 2 | 1.9891s | 64.35 token/s | 1.005 req/s | 0.9689GiB | | 4 | 2.4193s | 105.82 token/s | 1.653 req/s | 0.9776GiB | | 8 | 2.4094s | 212.50 token/s | 3.320 req/s | 0.9949GiB | ![1790589485093.png](/api-forum/file/RedirectImage?path=20260928/1790589506025_1790589485093.png '1790589485093.png') 我最关注的是两点。第一,批量从 1 增到 8 后,平均耗时没有按八倍增长,聚合吞吐提升很明显;第二,这个小模型在批量为 8 时峰值显存仍不到 1GiB。对教学实验和接口联调来说,试错成本很低。 当然,这并不意味着批量越大越好。真实业务还有输入长度差异、排队时间、首 token 延迟和显存碎片。这里能下的结论只有一个:当前海光环境的批量推理链路确实工作,而且还有很大的资源余量。 ## 接下来会拿来做什么 跑完这两轮后,我不会把 0.5B 模型包装成“可直接上线的智能助手”。它更像一把很方便的测试探针:模型小,错误暴露得快,适合检查环境、脚本和接口。 我计划继续在这台实例上做几类实验: - 把同一套脚本换成更大的 Qwen,观察质量和显存变化; - 对比 BF16 与 FP32 的速度和数值差异; - 加入动态批处理,记录首 token 延迟而不只看总吞吐; - 跑 OCR 或 YOLO 小模型,验证视觉推理链路; - 在 LoRA 训练前先检查数据加载、梯度和断点保存。 这次让我满意的不是某一句模型回答,而是现有 PyTorch/Transformers 代码几乎原样运行,Safetensors、BF16、批处理和显存统计都能接上。海光 BW 的 64GB 显存对 0.5B 明显富余,也给后续换大模型留出了空间。 真正需要我自己把关的,仍然是模型文件版本、测试方法和输出可信度。硬件把计算跑完只是第一步,工程结果能不能用,要看后面的验证是否足够认真。
全部评论(1)
只看楼主
暂无数据
--
楼主
Level.3
0 帖子
楼主热帖
暂无数据