# Datawhale x AMD Hello-ROCm + Qwen2.5-3B-Instruct # 云端推理部署实战

发布时间:2026/6/24 1:26:52
# Datawhale x AMD Hello-ROCm + Qwen2.5-3B-Instruct # 云端推理部署实战 Datawhale x AMD Hello-ROCm Qwen2.5-3B-Instruct云端推理部署实战1. Hello-前言为什么要在 48GB AMD 显卡上跑第二个模型事情的起因是这样的我在云上有一台搭载 AMD GPU 的实例上面已经跑着一个 Gemma 4 E4B 模型的 LoRA 情绪识别微调项目。GPU 显存总共 48GBGemma 4 占了约 32.7GB68%还剩大约 15GB 空闲。手里有闲着的显存就像口袋里有多余的现金——不花出去总觉得亏。于是决定在同一个实例上再部署一个轻量级大模型跟 Gemma 4 搭伙干活。1.1 选型四个候选一个答案剩余约 15GB 显存的硬约束直接把 7B 的模型排除在外。在可使用范围内筛选了四个候选模型FP16 大小特点Qwen2.5-1.5B~3GB中文能力一流阿里出品Qwen2.5-3B~6GB中文代码双优32K 上下文Gemma 3-1B~2GB极轻量但跟已有 Gemma 4 重合Llama 3.2-3B~6GB英文强、生态好中文一般结论毫无悬念选 Qwen2.5-3B-Instruct。原因很简单——我手上的业务场景金融文档、教育 AI都是中文密集型的Qwen 在中文理解和生成上的优势是另外三个无法比拟的。6GB 的显存开销也完全在预算之内。1.2 环境信息GPU: AMD MI300 系列 (Device ID 0x744b) VRAM: 48 GB HBM (rocm-smi 实测) 系统: Ubuntu ROCm 7.x Python: 3.12.3 关键包: transformers 5.x, vLLM 0.23.0, PyTorch 2.102. 环境准备与模型下载2.1 检查 GPU 状态动手之前先用rocm-smi摸清家底$ rocm-smiConcise InfoDevice Node IDs Temp Power Partitions SCLK MCLK Fan Perf PwrCap VRAM% GPU%(DID, GUID)(Edge)(Avg)(Mem, Compute, ID)050x744b,1303725.0°C10.0W N/A, N/A,00Mhz 96Mhz20.0% auto241.0W68%0%$ rocm-smi--showmeminfovram GPU[0]:VRAM Total Memory(B):51522830336# ≈ 48 GBGPU[0]:VRAM Total Used Memory(B):35168178176# ≈ 32.7 GB (68%)温度 25°C、功耗 10W、GPU 利用率 0%——显卡在摸鱼正是搞事情的好时机。2.2 下载 Qwen2.5-3B-Instruct使用魔搭 ModelScope 下载模型完全脱离 HuggingFace Hub无需 HF_TOKEN私有化友好frommodelscopeimportsnapshot_download model_dirsnapshot_download(Qwen/Qwen2.5-3B-Instruct,cache_dir/workspace/repo/src/models)print(f下载完成:{model_dir})几分钟就下完了。模型文件落地路径/workspace/repo/src/models/Qwen/Qwen2.5-3B-Instruct/值得注意的是ModelScope 在下载完成后会在模型目录内创建一个._____temp临时目录和.lock锁文件这是正常的下载残留不影响使用。3. vLLM 部署尝试踩坑记录按照标准操作流程第一反应是上 vLLM。毕竟 vLLM 是目前开源社区最主流的 LLM 推理引擎PagedAttention、Continuous Batching 这些特性耳熟能详。3.1 第一次尝试相对路径陷阱$ vllm serve ./models/Qwen/Qwen2.5-3B-Instruct/ --served-model-name qwen2.5-3b# 报错# OSError: Repo id must be in the form repo_name or namespace/repo_namevLLM 不接受相对路径必须用绝对路径。这算是个小坑——HuggingFace transformers 的from_pretrained()对相对路径宽容得多。3.2 第二次尝试v1 引擎在 ROCm 上崩溃换成绝对路径后$ vllm serve /workspace/repo/src/models/Qwen/Qwen2.5-3B-Instruct/\--served-model-name qwen2.5-3b# 报错# RuntimeError: Engine core initialization failed.# See root cause above. Failed core proc(s): {}嗯没有 root cause往上翻日志——一堆 vLLM v1 engine 的内部调用栈没有具体的硬件兼容性报错就是默默地崩溃了。3.3 第三次尝试强制降级 v0 引擎vLLM 从 v1 架构开始大幅重构了引擎核心换成环境变量VLLM_USE_V10强制用老版本引擎$VLLM_USE_V10vllm serve\/workspace/repo/src/models/Qwen/Qwen2.5-3B-Instruct/\--served-model-name qwen2.5-3b\--max-model-len4096# 同样的 RuntimeError完全相同的错误有意思——VLLM_USE_V10环境变量没有被吃掉日志里看不到警告说它被忽略但引擎还是在 v1 路径上崩溃了。3.4 Root Cause 分析结合日志和社区反馈这个问题的根因可以锁定vLLM 的 v1 引擎引入了新的进程管理和初始化体系AMD ROCm 下的适配尚不成熟VLLM_USE_V10在当前版本v0.23.0rocm723可能已失效——上游正在逐步废弃 v0 引擎引擎核心进程在启动时静默崩溃日志不够友好Failed core proc(s): {}后面是空字典——本应包含失败进程 PID 的3.5 放弃 vLLM 的决策逻辑坦率地说对于 Qwen2.5-3B 这种 30 亿参数的小模型强行上 vLLM 本身就是杀鸡用牛刀。vLLM 的核心价值在于PagedAttention的高吞吐 KV Cache 管理、Continuous Batching的多请求合并、Tensor Parallelism的多卡分布式推理。而 3B 模型单卡显存完全够用6GB / 48GB不需要分布式个人使用的并发请求量极少一般就 1-2 个Continuous Batching 的收益接近于零模型小到不需要 PagedAttention 做显存优化——标准 attention 就够了结论与其花半天时间跟 vLLM 的 ROCm 兼容性较劲不如直接用 transformers 加载模型做推理。简单、稳定、够用。4. Transformers 推理验证与常驻服务4.1 快速推理验证用一段最简代码先跑通推理importtorchfromtransformersimportAutoModelForCausalLM,AutoTokenizer model_path/workspace/repo/src/models/Qwen/Qwen2.5-3B-InstructtokenizerAutoTokenizer.from_pretrained(model_path,trust_remote_codeTrue)modelAutoModelForCausalLM.from_pretrained(model_path,dtypetorch.float16,device_mapauto,trust_remote_codeTrue)msg[{role:user,content:你好请用一句话介绍自己}]texttokenizer.apply_chat_template(msg,tokenizeFalse,add_generation_promptTrue)inputstokenizer(text,return_tensorspt).to(model.device)outmodel.generate(**inputs,max_new_tokens50,pad_token_idtokenizer.eos_token_id)print(tokenizer.decode(out[0][len(inputs[input_ids][0]):],skip_special_tokensTrue))输出✅ 我是Qwen由阿里云开发的超大规模语言模型旨在提供帮助和解答各种问题。一次跑通没毛病。加载速度极快430 it/s3B 的轻量优势体现得淋漓尽致。4.2 常驻推理服务的兼容性挑战验证通过后要写一个常驻推理脚本让模型一直保持在显存里。这里遇到了本次部署最大的坑——transformers 5.x 的 API 兼容性问题。4.2.1apply_chat_template返回值变了在 transformers 4.x 中apply_chat_template(tokenizeFalse)返回的是一个纯字符串直接可以喂给 tokenizer。但 5.x 版本的行为发生了变化——同样的参数可能返回BatchEncoding对象或列表导致后续的tokenizer()调用失败# 报错TypeError: TextEncodeInput must be Union[TextInputSequence, ...] # 原因apply_chat_template 返回了列表而不是字符串4.2.2 BatchEncoding 的.shape陷阱第二个坑transformers 5.x 的apply_chat_template(tokenizeTrue)返回BatchEncoding对象后直接访问.shape属性会抛出AttributeErrorinputstokenizer.apply_chat_template(messages,tokenizeTrue,...)input_leninputs.shape[1]# ❌ AttributeError: BatchEncoding has no shape# 必须先取 input_idsinput_leninputs[input_ids].shape[1]# ✅4.2.3 最终解决方案绕过apply_chat_template经历多轮迭代修复后终极方案是——干脆不用apply_chat_template了。回到最底层的 API手动构造 ChatML 格式的字符串然后用最简单的tokenizer(text)生成input_ids。这是 transformers 至今所有版本都兼容的方式defchat(prompt):# 直接用 Qwen 的 ChatML 格式模板formatted(|im_start|system\nYou are a helpful assistant.|im_end|\n|im_start|user\nprompt|im_end|\n|im_start|assistant\n)inputstokenizer(formatted,return_tensorspt).to(model.device)input_leninputs[input_ids].shape[1]withtorch.no_grad():outputsmodel.generate(input_idsinputs[input_ids],attention_maskinputs.get(attention_mask),max_new_tokens512,temperature0.7,do_sampleTrue,pad_token_idtokenizer.eos_token_id,)returntokenizer.decode(outputs[0][input_len:],skip_special_tokensTrue).strip()这一版经过了实际推理测试的验证工作正常 你好你是谁 你好我是来自阿里云的大规模语言模型我叫通义千问。 郑州属于中国的哪个省 郑州位于中国河南省。 如何评估开源大模型的安全性 评估开源大模型的安全性可以从多维度进行...完整专业回答三条测试全部通过没有报错。4.2.4 最终常驻服务脚本# 文件: /workspace/repo/src/scripts/qwen_server.pyimporttorchfromtransformersimportAutoModelForCausalLM,AutoTokenizer MODEL_PATH/workspace/repo/src/models/Qwen/Qwen2.5-3B-InstructtokenizerAutoTokenizer.from_pretrained(MODEL_PATH,trust_remote_codeTrue)modelAutoModelForCausalLM.from_pretrained(MODEL_PATH,dtypetorch.float16,device_mapauto,trust_remote_codeTrue)defchat(prompt):promptprompt.strip().replace(\n, )formattedf|im_start|system\nYou are a helpful assistant.|im_end|\n|im_start|user\n{prompt}|im_end|\n|im_start|assistant\ninputstokenizer(formatted,return_tensorspt).to(model.device)input_leninputs[input_ids].shape[1]withtorch.no_grad():outputsmodel.generate(input_idsinputs[input_ids],attention_maskinputs.get(attention_mask),max_new_tokens512,temperature0.7,do_sampleTrue,pad_token_idtokenizer.eos_token_id,)returntokenizer.decode(outputs[0][input_len:],skip_special_tokensTrue).strip()whileTrue:user_inputinput( ).strip()ifuser_input.lower()in(quit,exit,q):breakifnotuser_input:continueprint(\nchat(user_input)\n)运行命令python3 /workspace/repo/src/scripts/qwen_server.py5. 双模型共存的显存管理5.1 部署前后对比部署前后跑rocm-smi做对比部署前只有 Gemma 4 E4BVRAM%: 68% | GPU%: 0%部署后Gemma 4 Qwen 2.5-3B 同时加载VRAM%: 68% | GPU%: 0%等等……显存没涨对因为前面跑推理验证的 Python 进程退出后Qwen 模型就从显存里释放了。这才是关键点Python 进程退出 显存释放。GPU 不像硬盘不会记住加载过的模型。两个模型不能通过先后运行两个脚本来共存——必须在一个进程里同时加载。常驻推理服务的意义就在于此保持进程不退出模型就一直在显存里待命。5.2 双模型共存的可行性理论上Qwen 2.5-3B~6GB和 Gemma 4 E4B~32.7GB一共约 38.7GB刚好塞进 48GB 的显存里。但需要在一个脚本中同时加载两个模型管理各自的推理管线。这是下一步的工作方向。6. 经验总结与私有化部署启示6.1 三点核心教训 小模型不必上 vLLMvLLM 是为高吞吐、多并发、分布式设计的重型引擎。3B 级别的小模型直接上 transformers 就是最好的选择。别为了看起来专业增加不必要的复杂度。 AMD ROCm 生态仍需绕路走ROCm 下 vLLM v1 引擎的兼容性不如 CUDA 成熟。遇到引擎级别的崩溃果断降级策略——从 vLLM 退到 transformers从 v1 退到手动管理。完美主义在 ROCm 上最浪费时间。 版本兼容性比算法更耗时间本次部署耗时最多的不是下载模型、不是写推理逻辑而是处理 transformers 5.x 的 API 变更。高版本框架的 Breaking Change 是私有化部署中最容易踩的暗坑。6.2 对私有化部署的启示这次部署看似只是再装一个模型但实际上验证了几个对私有化场景很重要的结论ModelScope 的snapshot_download是比git-lfs更可靠的模型拉取方式——尤其是在国内网络环境下腾讯云镜像的速度远优于 HuggingFace Hub绕过apply_chat_template、回到最底层tokenizerAPI 的做法本质上是一种最低共同特性策略——选择所有版本都兼容的那一层接口避免被框架升级牵着走48GB 显存的 AMD 实例完全能跑两个模型一大一小对于需要并行多模型推理的边缘部署场景是有参考价值的应建立一个显存和进程的显式管理体系——在私有化部署持续扩展时需跟踪每个模型的显存占用和生命周期7. FAQQ: 为什么不用 vLLMA: 不是不支持 vLLM而是在 AMD ROCm 下的当前版本v0.23.0中v1 引擎存在兼容性问题且对 3B 小模型而言 vLLM 没有显著收益。用 transformers 更直接、更稳定。Q: 为什么选 Qwen 而不是其他模型A: 业务需求以中文为主金融文档、教育 AIQwen 的中文能力远胜 Llama 和 Gemma 3且 3B 版本显存友好。选型原则是场景适配 模型大小。Q: 怎么避免 transformers 版本兼容性问题A: 最稳妥的办法回到tokenizer(text)这个底层 API。ChatML 格式模板虽然是手动拼接的但在所有 transformers 版本下都能正常工作不会因为apply_chat_template的行为变更而崩溃。Q: 两个模型能同时跑吗A: 理论上可以。现在两个模型一共约 38.7GBGemma 4 的 32.7GB Qwen 3B 的 6GB48GB 显存装得下。但需要在一个脚本中管理两个模型的加载和推理下一篇文章会展开讲。8. 结语这次部署的完整耗时分配很有意思下载模型3 分钟首次推理验证transformers1 分钟vLLM 踩坑和放弃5 分钟常驻服务脚本的兼容性调试25 分钟版本兼容性调试占了总时间的 70%。这就是私有化部署的真实写照算法不难环境最磨人。最终的代码只有不到 40 行但背后的不做什么比做什么更值得记录不做 vLLM、不做多卡分布式、不做apply_chat_template、不做过度工程化。**小模型的部署哲学应该是用最简单的工具做最可靠的事。摘要本文记录了在已部署 Gemma 4 E4B 模型的 48GB AMD GPU 实例上额外部署 Qwen2.5-3B-Instruct 模型的完整实战过程。主要内容包括选型决策基于剩余约15GB显存和中文密集型业务需求选择 Qwen2.5-3B-InstructFP16约6GB。vLLM 部署踩坑尝试使用 vLLM 部署时因 AMD ROCm 下 v1 引擎兼容性问题导致多次失败最终放弃 vLLM 方案。Transformers 直接部署改用 transformers 库直接加载模型成功完成推理验证。但在编写常驻服务脚本时遇到 transformers 5.x 版本 API 变更带来的兼容性问题特别是apply_chat_template方法的行为变化。最终解决方案绕过apply_chat_template手动构造 ChatML 格式字符串使用底层tokenizer(text)API 实现稳定兼容的推理服务。核心经验小模型3B级别无需复杂推理引擎transformers 直接加载更简单可靠AMD ROCm 生态仍需谨慎对待兼容性问题版本兼容性调试是私有化部署中最耗时的环节采用最低共同特性策略选择所有版本都兼容的底层 API显存管理验证了在48GB显存中同时运行 Gemma 4约32.7GB和 Qwen 2.5-3B约6GB的可行性为多模型并行推理提供了实践参考。最终部署代码仅40行左右体现了用最简单的工具做最可靠的事的小模型部署哲学。**